ETH Price: $2,580.18 (-3.01%)

Transaction Decoder

Block:
21503361 at Dec-28-2024 08:30:47 PM +UTC
Transaction Fee:
0.000807491745662973 ETH $2.08
Gas Used:
172,137 Gas / 4.690983029 Gwei

Emitted Events:

287 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 00000000000000000000000000000000000000000052dd17befd56cfd5e51467, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000065b9427feac9a9e1b22662, 00000000000000000000000000000000000000000395e2ca373515ce3d6513d5, 000000000000000000000000000000000000000003afed7b4642dd8c36a6bbaa )
288 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000090404eb50ac6ec94ba90ecbcb9f808e9d73e40ec, 0x00000000000000000000000098c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c, 000000000000000000000000000000000000000000000000000000e8d4a51000 )
289 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000090404eb50ac6ec94ba90ecbcb9f808e9d73e40ec, 000000000000000000000000000000000000000000000000000000e8d4a6b1ad )
290 InitializableImmutableAdminUpgradeabilityProxy.0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196( 0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196, 0x00000000000000000000000090404eb50ac6ec94ba90ecbcb9f808e9d73e40ec, 0x00000000000000000000000090404eb50ac6ec94ba90ecbcb9f808e9d73e40ec, 000000000000000000000000000000000000000000000000000000e8d4a6b1ad, 000000000000000000000000000000000000000000000000000000000001a1ad, 00000000000000000000000000000000000000000395e2ca373515ce3d6513d5 )
291 InitializableImmutableAdminUpgradeabilityProxy.0x2b627736bca15cd5381dcf80b0bf11fd197d01a037c52b927a881a10fb73ba61( 0x2b627736bca15cd5381dcf80b0bf11fd197d01a037c52b927a881a10fb73ba61, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x00000000000000000000000090404eb50ac6ec94ba90ecbcb9f808e9d73e40ec, 0x0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000090404eb50ac6ec94ba90ecbcb9f808e9d73e40ec, 000000000000000000000000000000000000000000000000000000e8d4a51000 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
12.882703680399888403 Eth12.882720894099888403 Eth0.0000172137
0x87870Bca...50B4fA4E2
(Aave: Pool V3)
0x90404Eb5...9D73e40ec
0.446640419661758002 Eth
Nonce: 1383
0.445832927916095029 Eth
Nonce: 1384
0.000807491745662973
0x98C23E9d...2F4e16F5c
0xA0b86991...E3606eB48

Execution Trace

InitializableImmutableAdminUpgradeabilityProxy.617ba037( )
  • PoolInstance.supply( asset=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, amount=1000000000000, onBehalfOf=0x90404Eb50AC6eC94bA90ecBcB9f808e9D73e40ec, referralCode=0 )
    • SupplyLogic.1913f161( )
      • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
        • VariableDebtToken.DELEGATECALL( )
        • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
          • AToken.DELEGATECALL( )
          • DefaultReserveInterestRateStrategyV2.calculateInterestRates( params=[{name:unbacked, type:uint256, order:1, indexed:false, value:0, valueString:0}, {name:liquidityAdded, type:uint256, order:2, indexed:false, value:1000000000000, valueString:1000000000000}, {name:liquidityTaken, type:uint256, order:3, indexed:false, value:0, valueString:0}, {name:totalDebt, type:uint256, order:4, indexed:false, value:2065881733316244, valueString:2065881733316244}, {name:reserveFactor, type:uint256, order:5, indexed:false, value:1000, valueString:1000}, {name:reserve, type:address, order:6, indexed:false, value:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, valueString:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}, {name:usingVirtualBalance, type:bool, order:7, indexed:false, value:true, valueString:True}, {name:virtualUnderlyingBalance, type:uint256, order:8, indexed:false, value:215592921656292, valueString:215592921656292}] ) => ( 100175998238478966198703207, 122976372282918614680610402 )
          • FiatTokenProxy.23b872dd( )
            • FiatTokenV2_2.transferFrom( from=0x90404Eb50AC6eC94bA90ecBcB9f808e9D73e40ec, to=0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c, value=1000000000000 ) => ( True )
            • InitializableImmutableAdminUpgradeabilityProxy.b3f1c93d( )
              • AToken.mint( caller=0x90404Eb50AC6eC94bA90ecBcB9f808e9D73e40ec, onBehalfOf=0x90404Eb50AC6eC94bA90ecBcB9f808e9D73e40ec, amount=1000000000000, index=1109655961632156203742467029 ) => ( False )
                • InitializableImmutableAdminUpgradeabilityProxy.31873e2e( )
                  • RewardsController.handleAction( user=0x90404Eb50AC6eC94bA90ecBcB9f808e9D73e40ec, totalSupply=2055859354847856, userBalance=90214380 )
                    File 1 of 12: InitializableImmutableAdminUpgradeabilityProxy
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    /**
                     * @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.8.10;
                    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 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)
                        }
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    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.8.10;
                    /**
                     * @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.
                       * Will run if no other function in the contract matches the call data.
                       * 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());
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    import {BaseUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
                    /**
                     * @title BaseImmutableAdminUpgradeabilityProxy
                     * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
                     * @notice This contract combines an upgradeability proxy with an authorization
                     * mechanism for administrative tasks.
                     * @dev 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 internal immutable _admin;
                      /**
                       * @dev Constructor.
                       * @param admin The address of the admin
                       */
                      constructor(address admin) {
                        _admin = admin;
                      }
                      modifier ifAdmin() {
                        if (msg.sender == _admin) {
                          _;
                        } else {
                          _fallback();
                        }
                      }
                      /**
                       * @notice Return the admin address
                       * @return The address of the proxy admin.
                       */
                      function admin() external ifAdmin returns (address) {
                        return _admin;
                      }
                      /**
                       * @notice Return the implementation address
                       * @return The address of the implementation.
                       */
                      function implementation() external ifAdmin returns (address) {
                        return _implementation();
                      }
                      /**
                       * @notice Upgrade the backing implementation of the proxy.
                       * @dev Only the admin can call this function.
                       * @param newImplementation The address of the new implementation.
                       */
                      function upgradeTo(address newImplementation) external ifAdmin {
                        _upgradeTo(newImplementation);
                      }
                      /**
                       * @notice Upgrade the backing implementation of the proxy and call a function
                       * on the new implementation.
                       * @dev This is useful to initialize the proxied contract.
                       * @param newImplementation The 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);
                      }
                      /**
                       * @notice 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.8.10;
                    import {InitializableUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
                    import {Proxy} from '../../../dependencies/openzeppelin/upgradeability/Proxy.sol';
                    import {BaseImmutableAdminUpgradeabilityProxy} from './BaseImmutableAdminUpgradeabilityProxy.sol';
                    /**
                     * @title InitializableAdminUpgradeabilityProxy
                     * @author Aave
                     * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
                     */
                    contract InitializableImmutableAdminUpgradeabilityProxy is
                      BaseImmutableAdminUpgradeabilityProxy,
                      InitializableUpgradeabilityProxy
                    {
                      /**
                       * @dev Constructor.
                       * @param admin The address of the admin
                       */
                      constructor(address admin) BaseImmutableAdminUpgradeabilityProxy(admin) {
                        // Intentionally left blank
                      }
                      /// @inheritdoc BaseImmutableAdminUpgradeabilityProxy
                      function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                        BaseImmutableAdminUpgradeabilityProxy._willFallback();
                      }
                    }
                    

                    File 2 of 12: FiatTokenProxy
                    pragma solidity ^0.4.24;
                    
                    // File: zos-lib/contracts/upgradeability/Proxy.sol
                    
                    /**
                     * @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.
                     */
                    contract Proxy {
                      /**
                       * @dev Fallback function.
                       * Implemented entirely in `_fallback`.
                       */
                      function () payable external {
                        _fallback();
                      }
                    
                      /**
                       * @return The Address of the implementation.
                       */
                      function _implementation() internal 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 {
                        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 {
                      }
                    
                      /**
                       * @dev fallback implementation.
                       * Extracted to enable manual triggering.
                       */
                      function _fallback() internal {
                        _willFallback();
                        _delegate(_implementation());
                      }
                    }
                    
                    // File: openzeppelin-solidity/contracts/AddressUtils.sol
                    
                    /**
                     * Utility library of inline functions on addresses
                     */
                    library AddressUtils {
                    
                      /**
                       * Returns whether the target address is a contract
                       * @dev This function will return false if invoked during the constructor of a contract,
                       * as the code is not actually created until after the constructor finishes.
                       * @param addr address to check
                       * @return whether the target address is a contract
                       */
                      function isContract(address addr) internal view returns (bool) {
                        uint256 size;
                        // XXX Currently there is no better way to check if there is a contract in an address
                        // than to check the size of the code at that address.
                        // See https://ethereum.stackexchange.com/a/14016/36603
                        // for more details about how this works.
                        // TODO Check this again before the Serenity release, because all addresses will be
                        // contracts then.
                        // solium-disable-next-line security/no-inline-assembly
                        assembly { size := extcodesize(addr) }
                        return size > 0;
                      }
                    
                    }
                    
                    // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
                    
                    /**
                     * @title UpgradeabilityProxy
                     * @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 UpgradeabilityProxy is Proxy {
                      /**
                       * @dev Emitted when the implementation is upgraded.
                       * @param implementation Address of the new implementation.
                       */
                      event Upgraded(address implementation);
                    
                      /**
                       * @dev Storage slot with the address of the current implementation.
                       * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
                       * validated in the constructor.
                       */
                      bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
                    
                      /**
                       * @dev Contract constructor.
                       * @param _implementation Address of the initial implementation.
                       */
                      constructor(address _implementation) public {
                        assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
                    
                        _setImplementation(_implementation);
                      }
                    
                      /**
                       * @dev Returns the current implementation.
                       * @return Address of the current implementation
                       */
                      function _implementation() internal view returns (address impl) {
                        bytes32 slot = IMPLEMENTATION_SLOT;
                        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) private {
                        require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
                    
                        bytes32 slot = IMPLEMENTATION_SLOT;
                    
                        assembly {
                          sstore(slot, newImplementation)
                        }
                      }
                    }
                    
                    // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
                    
                    /**
                     * @title AdminUpgradeabilityProxy
                     * @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 AdminUpgradeabilityProxy is UpgradeabilityProxy {
                      /**
                       * @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 "org.zeppelinos.proxy.admin", and is
                       * validated in the constructor.
                       */
                      bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
                    
                      /**
                       * @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();
                        }
                      }
                    
                      /**
                       * Contract constructor.
                       * It sets the `msg.sender` as the proxy administrator.
                       * @param _implementation address of the initial implementation.
                       */
                      constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
                        assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
                    
                        _setAdmin(msg.sender);
                      }
                    
                      /**
                       * @return The address of the proxy admin.
                       */
                      function admin() external view ifAdmin returns (address) {
                        return _admin();
                      }
                    
                      /**
                       * @return The address of the implementation.
                       */
                      function implementation() external view 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/develop/abi-spec.html#function-selector-and-argument-encoding.
                       */
                      function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
                        _upgradeTo(newImplementation);
                        require(address(this).call.value(msg.value)(data));
                      }
                    
                      /**
                       * @return The admin slot.
                       */
                      function _admin() internal view returns (address adm) {
                        bytes32 slot = ADMIN_SLOT;
                        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;
                    
                        assembly {
                          sstore(slot, newAdmin)
                        }
                      }
                    
                      /**
                       * @dev Only fall back when the sender is not the admin.
                       */
                      function _willFallback() internal {
                        require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                        super._willFallback();
                      }
                    }
                    
                    // File: contracts/FiatTokenProxy.sol
                    
                    /**
                    * Copyright CENTRE SECZ 2018
                    *
                    * Permission is hereby granted, free of charge, to any person obtaining a copy 
                    * of this software and associated documentation files (the "Software"), to deal 
                    * in the Software without restriction, including without limitation the rights 
                    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
                    * copies of the Software, and to permit persons to whom the Software is furnished to 
                    * do so, subject to the following conditions:
                    *
                    * The above copyright notice and this permission notice shall be included in all 
                    * copies or substantial portions of the Software.
                    *
                    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
                    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
                    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
                    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
                    * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
                    * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
                    */
                    
                    pragma solidity ^0.4.24;
                    
                    
                    /**
                     * @title FiatTokenProxy
                     * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
                    */ 
                    contract FiatTokenProxy is AdminUpgradeabilityProxy {
                        constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
                        }
                    }

                    File 3 of 12: InitializableImmutableAdminUpgradeabilityProxy
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    /**
                     * @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.8.10;
                    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 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)
                        }
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    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.8.10;
                    /**
                     * @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.
                       * Will run if no other function in the contract matches the call data.
                       * 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());
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    import {BaseUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
                    /**
                     * @title BaseImmutableAdminUpgradeabilityProxy
                     * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
                     * @notice This contract combines an upgradeability proxy with an authorization
                     * mechanism for administrative tasks.
                     * @dev 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 internal immutable _admin;
                      /**
                       * @dev Constructor.
                       * @param admin The address of the admin
                       */
                      constructor(address admin) {
                        _admin = admin;
                      }
                      modifier ifAdmin() {
                        if (msg.sender == _admin) {
                          _;
                        } else {
                          _fallback();
                        }
                      }
                      /**
                       * @notice Return the admin address
                       * @return The address of the proxy admin.
                       */
                      function admin() external ifAdmin returns (address) {
                        return _admin;
                      }
                      /**
                       * @notice Return the implementation address
                       * @return The address of the implementation.
                       */
                      function implementation() external ifAdmin returns (address) {
                        return _implementation();
                      }
                      /**
                       * @notice Upgrade the backing implementation of the proxy.
                       * @dev Only the admin can call this function.
                       * @param newImplementation The address of the new implementation.
                       */
                      function upgradeTo(address newImplementation) external ifAdmin {
                        _upgradeTo(newImplementation);
                      }
                      /**
                       * @notice Upgrade the backing implementation of the proxy and call a function
                       * on the new implementation.
                       * @dev This is useful to initialize the proxied contract.
                       * @param newImplementation The 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);
                      }
                      /**
                       * @notice 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.8.10;
                    import {InitializableUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
                    import {Proxy} from '../../../dependencies/openzeppelin/upgradeability/Proxy.sol';
                    import {BaseImmutableAdminUpgradeabilityProxy} from './BaseImmutableAdminUpgradeabilityProxy.sol';
                    /**
                     * @title InitializableAdminUpgradeabilityProxy
                     * @author Aave
                     * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
                     */
                    contract InitializableImmutableAdminUpgradeabilityProxy is
                      BaseImmutableAdminUpgradeabilityProxy,
                      InitializableUpgradeabilityProxy
                    {
                      /**
                       * @dev Constructor.
                       * @param admin The address of the admin
                       */
                      constructor(address admin) BaseImmutableAdminUpgradeabilityProxy(admin) {
                        // Intentionally left blank
                      }
                      /// @inheritdoc BaseImmutableAdminUpgradeabilityProxy
                      function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                        BaseImmutableAdminUpgradeabilityProxy._willFallback();
                      }
                    }
                    

                    File 4 of 12: PoolInstance
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    import {Pool} from '../protocol/pool/Pool.sol';
                    import {IPoolAddressesProvider} from '../interfaces/IPoolAddressesProvider.sol';
                    import {Errors} from '../protocol/libraries/helpers/Errors.sol';
                    contract PoolInstance is Pool {
                      uint256 public constant POOL_REVISION = 6;
                      constructor(IPoolAddressesProvider provider) Pool(provider) {}
                      /**
                       * @notice Initializes the Pool.
                       * @dev Function is invoked by the proxy contract when the Pool contract is added to the
                       * PoolAddressesProvider of the market.
                       * @dev Caching the address of the PoolAddressesProvider in order to reduce gas consumption on subsequent operations
                       * @param provider The address of the PoolAddressesProvider
                       */
                      function initialize(IPoolAddressesProvider provider) external virtual override initializer {
                        require(provider == ADDRESSES_PROVIDER, Errors.INVALID_ADDRESSES_PROVIDER);
                      }
                      function getRevision() internal pure virtual override returns (uint256) {
                        return POOL_REVISION;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {VersionedInitializable} from '../../misc/aave-upgradeability/VersionedInitializable.sol';
                    import {Errors} from '../libraries/helpers/Errors.sol';
                    import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
                    import {PoolLogic} from '../libraries/logic/PoolLogic.sol';
                    import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
                    import {EModeLogic} from '../libraries/logic/EModeLogic.sol';
                    import {SupplyLogic} from '../libraries/logic/SupplyLogic.sol';
                    import {FlashLoanLogic} from '../libraries/logic/FlashLoanLogic.sol';
                    import {BorrowLogic} from '../libraries/logic/BorrowLogic.sol';
                    import {LiquidationLogic} from '../libraries/logic/LiquidationLogic.sol';
                    import {DataTypes} from '../libraries/types/DataTypes.sol';
                    import {BridgeLogic} from '../libraries/logic/BridgeLogic.sol';
                    import {IERC20WithPermit} from '../../interfaces/IERC20WithPermit.sol';
                    import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol';
                    import {IPool} from '../../interfaces/IPool.sol';
                    import {IACLManager} from '../../interfaces/IACLManager.sol';
                    import {PoolStorage} from './PoolStorage.sol';
                    /**
                     * @title Pool contract
                     * @author Aave
                     * @notice Main point of interaction with an Aave protocol's market
                     * - Users can:
                     *   # Supply
                     *   # Withdraw
                     *   # Borrow
                     *   # Repay
                     *   # Enable/disable their supplied assets as collateral
                     *   # Liquidate positions
                     *   # Execute Flash Loans
                     * @dev To be covered by a proxy contract, owned by the PoolAddressesProvider of the specific market
                     * @dev All admin functions are callable by the PoolConfigurator contract defined also in the
                     *   PoolAddressesProvider
                     */
                    abstract contract Pool is VersionedInitializable, PoolStorage, IPool {
                      using ReserveLogic for DataTypes.ReserveData;
                      IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
                      /**
                       * @dev Only pool configurator can call functions marked by this modifier.
                       */
                      modifier onlyPoolConfigurator() {
                        _onlyPoolConfigurator();
                        _;
                      }
                      /**
                       * @dev Only pool admin can call functions marked by this modifier.
                       */
                      modifier onlyPoolAdmin() {
                        _onlyPoolAdmin();
                        _;
                      }
                      /**
                       * @dev Only bridge can call functions marked by this modifier.
                       */
                      modifier onlyBridge() {
                        _onlyBridge();
                        _;
                      }
                      function _onlyPoolConfigurator() internal view virtual {
                        require(
                          ADDRESSES_PROVIDER.getPoolConfigurator() == msg.sender,
                          Errors.CALLER_NOT_POOL_CONFIGURATOR
                        );
                      }
                      function _onlyPoolAdmin() internal view virtual {
                        require(
                          IACLManager(ADDRESSES_PROVIDER.getACLManager()).isPoolAdmin(msg.sender),
                          Errors.CALLER_NOT_POOL_ADMIN
                        );
                      }
                      function _onlyBridge() internal view virtual {
                        require(
                          IACLManager(ADDRESSES_PROVIDER.getACLManager()).isBridge(msg.sender),
                          Errors.CALLER_NOT_BRIDGE
                        );
                      }
                      /**
                       * @dev Constructor.
                       * @param provider The address of the PoolAddressesProvider contract
                       */
                      constructor(IPoolAddressesProvider provider) {
                        ADDRESSES_PROVIDER = provider;
                      }
                      /**
                       * @notice Initializes the Pool.
                       * @dev Function is invoked by the proxy contract when the Pool contract is added to the
                       * PoolAddressesProvider of the market.
                       * @dev Caching the address of the PoolAddressesProvider in order to reduce gas consumption on subsequent operations
                       * @param provider The address of the PoolAddressesProvider
                       */
                      function initialize(IPoolAddressesProvider provider) external virtual;
                      /// @inheritdoc IPool
                      function mintUnbacked(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external virtual override onlyBridge {
                        BridgeLogic.executeMintUnbacked(
                          _reserves,
                          _reservesList,
                          _usersConfig[onBehalfOf],
                          asset,
                          amount,
                          onBehalfOf,
                          referralCode
                        );
                      }
                      /// @inheritdoc IPool
                      function backUnbacked(
                        address asset,
                        uint256 amount,
                        uint256 fee
                      ) external virtual override onlyBridge returns (uint256) {
                        return
                          BridgeLogic.executeBackUnbacked(_reserves[asset], asset, amount, fee, _bridgeProtocolFee);
                      }
                      /// @inheritdoc IPool
                      function supply(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) public virtual override {
                        SupplyLogic.executeSupply(
                          _reserves,
                          _reservesList,
                          _usersConfig[onBehalfOf],
                          DataTypes.ExecuteSupplyParams({
                            asset: asset,
                            amount: amount,
                            onBehalfOf: onBehalfOf,
                            referralCode: referralCode
                          })
                        );
                      }
                      /// @inheritdoc IPool
                      function supplyWithPermit(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) public virtual override {
                        try
                          IERC20WithPermit(asset).permit(
                            msg.sender,
                            address(this),
                            amount,
                            deadline,
                            permitV,
                            permitR,
                            permitS
                          )
                        {} catch {}
                        SupplyLogic.executeSupply(
                          _reserves,
                          _reservesList,
                          _usersConfig[onBehalfOf],
                          DataTypes.ExecuteSupplyParams({
                            asset: asset,
                            amount: amount,
                            onBehalfOf: onBehalfOf,
                            referralCode: referralCode
                          })
                        );
                      }
                      /// @inheritdoc IPool
                      function withdraw(
                        address asset,
                        uint256 amount,
                        address to
                      ) public virtual override returns (uint256) {
                        return
                          SupplyLogic.executeWithdraw(
                            _reserves,
                            _reservesList,
                            _eModeCategories,
                            _usersConfig[msg.sender],
                            DataTypes.ExecuteWithdrawParams({
                              asset: asset,
                              amount: amount,
                              to: to,
                              reservesCount: _reservesCount,
                              oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                              userEModeCategory: _usersEModeCategory[msg.sender]
                            })
                          );
                      }
                      /// @inheritdoc IPool
                      function borrow(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode,
                        uint16 referralCode,
                        address onBehalfOf
                      ) public virtual override {
                        BorrowLogic.executeBorrow(
                          _reserves,
                          _reservesList,
                          _eModeCategories,
                          _usersConfig[onBehalfOf],
                          DataTypes.ExecuteBorrowParams({
                            asset: asset,
                            user: msg.sender,
                            onBehalfOf: onBehalfOf,
                            amount: amount,
                            interestRateMode: DataTypes.InterestRateMode(interestRateMode),
                            referralCode: referralCode,
                            releaseUnderlying: true,
                            reservesCount: _reservesCount,
                            oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                            userEModeCategory: _usersEModeCategory[onBehalfOf],
                            priceOracleSentinel: ADDRESSES_PROVIDER.getPriceOracleSentinel()
                          })
                        );
                      }
                      /// @inheritdoc IPool
                      function repay(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode,
                        address onBehalfOf
                      ) public virtual override returns (uint256) {
                        return
                          BorrowLogic.executeRepay(
                            _reserves,
                            _reservesList,
                            _usersConfig[onBehalfOf],
                            DataTypes.ExecuteRepayParams({
                              asset: asset,
                              amount: amount,
                              interestRateMode: DataTypes.InterestRateMode(interestRateMode),
                              onBehalfOf: onBehalfOf,
                              useATokens: false
                            })
                          );
                      }
                      /// @inheritdoc IPool
                      function repayWithPermit(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode,
                        address onBehalfOf,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) public virtual override returns (uint256) {
                        try
                          IERC20WithPermit(asset).permit(
                            msg.sender,
                            address(this),
                            amount,
                            deadline,
                            permitV,
                            permitR,
                            permitS
                          )
                        {} catch {}
                        {
                          DataTypes.ExecuteRepayParams memory params = DataTypes.ExecuteRepayParams({
                            asset: asset,
                            amount: amount,
                            interestRateMode: DataTypes.InterestRateMode(interestRateMode),
                            onBehalfOf: onBehalfOf,
                            useATokens: false
                          });
                          return BorrowLogic.executeRepay(_reserves, _reservesList, _usersConfig[onBehalfOf], params);
                        }
                      }
                      /// @inheritdoc IPool
                      function repayWithATokens(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode
                      ) public virtual override returns (uint256) {
                        return
                          BorrowLogic.executeRepay(
                            _reserves,
                            _reservesList,
                            _usersConfig[msg.sender],
                            DataTypes.ExecuteRepayParams({
                              asset: asset,
                              amount: amount,
                              interestRateMode: DataTypes.InterestRateMode(interestRateMode),
                              onBehalfOf: msg.sender,
                              useATokens: true
                            })
                          );
                      }
                      /// @inheritdoc IPool
                      function setUserUseReserveAsCollateral(
                        address asset,
                        bool useAsCollateral
                      ) public virtual override {
                        SupplyLogic.executeUseReserveAsCollateral(
                          _reserves,
                          _reservesList,
                          _eModeCategories,
                          _usersConfig[msg.sender],
                          asset,
                          useAsCollateral,
                          _reservesCount,
                          ADDRESSES_PROVIDER.getPriceOracle(),
                          _usersEModeCategory[msg.sender]
                        );
                      }
                      /// @inheritdoc IPool
                      function liquidationCall(
                        address collateralAsset,
                        address debtAsset,
                        address user,
                        uint256 debtToCover,
                        bool receiveAToken
                      ) public virtual override {
                        LiquidationLogic.executeLiquidationCall(
                          _reserves,
                          _reservesList,
                          _usersConfig,
                          _eModeCategories,
                          DataTypes.ExecuteLiquidationCallParams({
                            reservesCount: _reservesCount,
                            debtToCover: debtToCover,
                            collateralAsset: collateralAsset,
                            debtAsset: debtAsset,
                            user: user,
                            receiveAToken: receiveAToken,
                            priceOracle: ADDRESSES_PROVIDER.getPriceOracle(),
                            userEModeCategory: _usersEModeCategory[user],
                            priceOracleSentinel: ADDRESSES_PROVIDER.getPriceOracleSentinel()
                          })
                        );
                      }
                      /// @inheritdoc IPool
                      function flashLoan(
                        address receiverAddress,
                        address[] calldata assets,
                        uint256[] calldata amounts,
                        uint256[] calldata interestRateModes,
                        address onBehalfOf,
                        bytes calldata params,
                        uint16 referralCode
                      ) public virtual override {
                        DataTypes.FlashloanParams memory flashParams = DataTypes.FlashloanParams({
                          receiverAddress: receiverAddress,
                          assets: assets,
                          amounts: amounts,
                          interestRateModes: interestRateModes,
                          onBehalfOf: onBehalfOf,
                          params: params,
                          referralCode: referralCode,
                          flashLoanPremiumToProtocol: _flashLoanPremiumToProtocol,
                          flashLoanPremiumTotal: _flashLoanPremiumTotal,
                          reservesCount: _reservesCount,
                          addressesProvider: address(ADDRESSES_PROVIDER),
                          pool: address(this),
                          userEModeCategory: _usersEModeCategory[onBehalfOf],
                          isAuthorizedFlashBorrower: IACLManager(ADDRESSES_PROVIDER.getACLManager()).isFlashBorrower(
                            msg.sender
                          )
                        });
                        FlashLoanLogic.executeFlashLoan(
                          _reserves,
                          _reservesList,
                          _eModeCategories,
                          _usersConfig[onBehalfOf],
                          flashParams
                        );
                      }
                      /// @inheritdoc IPool
                      function flashLoanSimple(
                        address receiverAddress,
                        address asset,
                        uint256 amount,
                        bytes calldata params,
                        uint16 referralCode
                      ) public virtual override {
                        DataTypes.FlashloanSimpleParams memory flashParams = DataTypes.FlashloanSimpleParams({
                          receiverAddress: receiverAddress,
                          asset: asset,
                          amount: amount,
                          params: params,
                          referralCode: referralCode,
                          flashLoanPremiumToProtocol: _flashLoanPremiumToProtocol,
                          flashLoanPremiumTotal: _flashLoanPremiumTotal
                        });
                        FlashLoanLogic.executeFlashLoanSimple(_reserves[asset], flashParams);
                      }
                      /// @inheritdoc IPool
                      function mintToTreasury(address[] calldata assets) external virtual override {
                        PoolLogic.executeMintToTreasury(_reserves, assets);
                      }
                      /// @inheritdoc IPool
                      function getReserveDataExtended(
                        address asset
                      ) external view returns (DataTypes.ReserveData memory) {
                        return _reserves[asset];
                      }
                      /// @inheritdoc IPool
                      function getReserveData(
                        address asset
                      ) external view virtual override returns (DataTypes.ReserveDataLegacy memory) {
                        DataTypes.ReserveData memory reserve = _reserves[asset];
                        DataTypes.ReserveDataLegacy memory res;
                        res.configuration = reserve.configuration;
                        res.liquidityIndex = reserve.liquidityIndex;
                        res.currentLiquidityRate = reserve.currentLiquidityRate;
                        res.variableBorrowIndex = reserve.variableBorrowIndex;
                        res.currentVariableBorrowRate = reserve.currentVariableBorrowRate;
                        res.lastUpdateTimestamp = reserve.lastUpdateTimestamp;
                        res.id = reserve.id;
                        res.aTokenAddress = reserve.aTokenAddress;
                        res.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
                        res.interestRateStrategyAddress = reserve.interestRateStrategyAddress;
                        res.accruedToTreasury = reserve.accruedToTreasury;
                        res.unbacked = reserve.unbacked;
                        res.isolationModeTotalDebt = reserve.isolationModeTotalDebt;
                        // This is a temporary workaround for integrations that are broken by Aave 3.2
                        // While the new pool data provider is backward compatible, some integrations hard-code an old implementation
                        // To allow them to not have any infrastructural blocker, a mock must be configured in the Aave Pool Addresses Provider, returning zero on all required view methods, instead of reverting
                        res.stableDebtTokenAddress = ADDRESSES_PROVIDER.getAddress(bytes32('MOCK_STABLE_DEBT'));
                        return res;
                      }
                      /// @inheritdoc IPool
                      function getVirtualUnderlyingBalance(
                        address asset
                      ) external view virtual override returns (uint128) {
                        return _reserves[asset].virtualUnderlyingBalance;
                      }
                      /// @inheritdoc IPool
                      function getUserAccountData(
                        address user
                      )
                        external
                        view
                        virtual
                        override
                        returns (
                          uint256 totalCollateralBase,
                          uint256 totalDebtBase,
                          uint256 availableBorrowsBase,
                          uint256 currentLiquidationThreshold,
                          uint256 ltv,
                          uint256 healthFactor
                        )
                      {
                        return
                          PoolLogic.executeGetUserAccountData(
                            _reserves,
                            _reservesList,
                            _eModeCategories,
                            DataTypes.CalculateUserAccountDataParams({
                              userConfig: _usersConfig[user],
                              reservesCount: _reservesCount,
                              user: user,
                              oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                              userEModeCategory: _usersEModeCategory[user]
                            })
                          );
                      }
                      /// @inheritdoc IPool
                      function getConfiguration(
                        address asset
                      ) external view virtual override returns (DataTypes.ReserveConfigurationMap memory) {
                        return _reserves[asset].configuration;
                      }
                      /// @inheritdoc IPool
                      function getUserConfiguration(
                        address user
                      ) external view virtual override returns (DataTypes.UserConfigurationMap memory) {
                        return _usersConfig[user];
                      }
                      /// @inheritdoc IPool
                      function getReserveNormalizedIncome(
                        address asset
                      ) external view virtual override returns (uint256) {
                        return _reserves[asset].getNormalizedIncome();
                      }
                      /// @inheritdoc IPool
                      function getReserveNormalizedVariableDebt(
                        address asset
                      ) external view virtual override returns (uint256) {
                        return _reserves[asset].getNormalizedDebt();
                      }
                      /// @inheritdoc IPool
                      function getReservesList() external view virtual override returns (address[] memory) {
                        uint256 reservesListCount = _reservesCount;
                        uint256 droppedReservesCount = 0;
                        address[] memory reservesList = new address[](reservesListCount);
                        for (uint256 i = 0; i < reservesListCount; i++) {
                          if (_reservesList[i] != address(0)) {
                            reservesList[i - droppedReservesCount] = _reservesList[i];
                          } else {
                            droppedReservesCount++;
                          }
                        }
                        // Reduces the length of the reserves array by `droppedReservesCount`
                        assembly {
                          mstore(reservesList, sub(reservesListCount, droppedReservesCount))
                        }
                        return reservesList;
                      }
                      /// @inheritdoc IPool
                      function getReservesCount() external view virtual override returns (uint256) {
                        return _reservesCount;
                      }
                      /// @inheritdoc IPool
                      function getReserveAddressById(uint16 id) external view returns (address) {
                        return _reservesList[id];
                      }
                      /// @inheritdoc IPool
                      function BRIDGE_PROTOCOL_FEE() public view virtual override returns (uint256) {
                        return _bridgeProtocolFee;
                      }
                      /// @inheritdoc IPool
                      function FLASHLOAN_PREMIUM_TOTAL() public view virtual override returns (uint128) {
                        return _flashLoanPremiumTotal;
                      }
                      /// @inheritdoc IPool
                      function FLASHLOAN_PREMIUM_TO_PROTOCOL() public view virtual override returns (uint128) {
                        return _flashLoanPremiumToProtocol;
                      }
                      /// @inheritdoc IPool
                      function MAX_NUMBER_RESERVES() public view virtual override returns (uint16) {
                        return ReserveConfiguration.MAX_RESERVES_COUNT;
                      }
                      /// @inheritdoc IPool
                      function finalizeTransfer(
                        address asset,
                        address from,
                        address to,
                        uint256 amount,
                        uint256 balanceFromBefore,
                        uint256 balanceToBefore
                      ) external virtual override {
                        require(msg.sender == _reserves[asset].aTokenAddress, Errors.CALLER_NOT_ATOKEN);
                        SupplyLogic.executeFinalizeTransfer(
                          _reserves,
                          _reservesList,
                          _eModeCategories,
                          _usersConfig,
                          DataTypes.FinalizeTransferParams({
                            asset: asset,
                            from: from,
                            to: to,
                            amount: amount,
                            balanceFromBefore: balanceFromBefore,
                            balanceToBefore: balanceToBefore,
                            reservesCount: _reservesCount,
                            oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                            fromEModeCategory: _usersEModeCategory[from]
                          })
                        );
                      }
                      /// @inheritdoc IPool
                      function initReserve(
                        address asset,
                        address aTokenAddress,
                        address variableDebtAddress,
                        address interestRateStrategyAddress
                      ) external virtual override onlyPoolConfigurator {
                        if (
                          PoolLogic.executeInitReserve(
                            _reserves,
                            _reservesList,
                            DataTypes.InitReserveParams({
                              asset: asset,
                              aTokenAddress: aTokenAddress,
                              variableDebtAddress: variableDebtAddress,
                              interestRateStrategyAddress: interestRateStrategyAddress,
                              reservesCount: _reservesCount,
                              maxNumberReserves: MAX_NUMBER_RESERVES()
                            })
                          )
                        ) {
                          _reservesCount++;
                        }
                      }
                      /// @inheritdoc IPool
                      function dropReserve(address asset) external virtual override onlyPoolConfigurator {
                        PoolLogic.executeDropReserve(_reserves, _reservesList, asset);
                      }
                      /// @inheritdoc IPool
                      function setReserveInterestRateStrategyAddress(
                        address asset,
                        address rateStrategyAddress
                      ) external virtual override onlyPoolConfigurator {
                        require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                        require(_reserves[asset].id != 0 || _reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                        _reserves[asset].interestRateStrategyAddress = rateStrategyAddress;
                      }
                      /// @inheritdoc IPool
                      function syncIndexesState(address asset) external virtual override onlyPoolConfigurator {
                        DataTypes.ReserveData storage reserve = _reserves[asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        reserve.updateState(reserveCache);
                      }
                      /// @inheritdoc IPool
                      function syncRatesState(address asset) external virtual override onlyPoolConfigurator {
                        DataTypes.ReserveData storage reserve = _reserves[asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        ReserveLogic.updateInterestRatesAndVirtualBalance(reserve, reserveCache, asset, 0, 0);
                      }
                      /// @inheritdoc IPool
                      function setConfiguration(
                        address asset,
                        DataTypes.ReserveConfigurationMap calldata configuration
                      ) external virtual override onlyPoolConfigurator {
                        require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                        require(_reserves[asset].id != 0 || _reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                        _reserves[asset].configuration = configuration;
                      }
                      /// @inheritdoc IPool
                      function updateBridgeProtocolFee(
                        uint256 protocolFee
                      ) external virtual override onlyPoolConfigurator {
                        _bridgeProtocolFee = protocolFee;
                      }
                      /// @inheritdoc IPool
                      function updateFlashloanPremiums(
                        uint128 flashLoanPremiumTotal,
                        uint128 flashLoanPremiumToProtocol
                      ) external virtual override onlyPoolConfigurator {
                        _flashLoanPremiumTotal = flashLoanPremiumTotal;
                        _flashLoanPremiumToProtocol = flashLoanPremiumToProtocol;
                      }
                      /// @inheritdoc IPool
                      function configureEModeCategory(
                        uint8 id,
                        DataTypes.EModeCategoryBaseConfiguration memory category
                      ) external virtual override onlyPoolConfigurator {
                        // category 0 is reserved for volatile heterogeneous assets and it's always disabled
                        require(id != 0, Errors.EMODE_CATEGORY_RESERVED);
                        _eModeCategories[id].ltv = category.ltv;
                        _eModeCategories[id].liquidationThreshold = category.liquidationThreshold;
                        _eModeCategories[id].liquidationBonus = category.liquidationBonus;
                        _eModeCategories[id].label = category.label;
                      }
                      /// @inheritdoc IPool
                      function configureEModeCategoryCollateralBitmap(
                        uint8 id,
                        uint128 collateralBitmap
                      ) external virtual override onlyPoolConfigurator {
                        // category 0 is reserved for volatile heterogeneous assets and it's always disabled
                        require(id != 0, Errors.EMODE_CATEGORY_RESERVED);
                        _eModeCategories[id].collateralBitmap = collateralBitmap;
                      }
                      /// @inheritdoc IPool
                      function configureEModeCategoryBorrowableBitmap(
                        uint8 id,
                        uint128 borrowableBitmap
                      ) external virtual override onlyPoolConfigurator {
                        // category 0 is reserved for volatile heterogeneous assets and it's always disabled
                        require(id != 0, Errors.EMODE_CATEGORY_RESERVED);
                        _eModeCategories[id].borrowableBitmap = borrowableBitmap;
                      }
                      /// @inheritdoc IPool
                      function getEModeCategoryData(
                        uint8 id
                      ) external view virtual override returns (DataTypes.EModeCategoryLegacy memory) {
                        DataTypes.EModeCategory memory category = _eModeCategories[id];
                        return
                          DataTypes.EModeCategoryLegacy({
                            ltv: category.ltv,
                            liquidationThreshold: category.liquidationThreshold,
                            liquidationBonus: category.liquidationBonus,
                            priceSource: address(0),
                            label: category.label
                          });
                      }
                      /// @inheritdoc IPool
                      function getEModeCategoryCollateralConfig(
                        uint8 id
                      ) external view returns (DataTypes.CollateralConfig memory) {
                        return
                          DataTypes.CollateralConfig({
                            ltv: _eModeCategories[id].ltv,
                            liquidationThreshold: _eModeCategories[id].liquidationThreshold,
                            liquidationBonus: _eModeCategories[id].liquidationBonus
                          });
                      }
                      /// @inheritdoc IPool
                      function getEModeCategoryLabel(uint8 id) external view returns (string memory) {
                        return _eModeCategories[id].label;
                      }
                      /// @inheritdoc IPool
                      function getEModeCategoryCollateralBitmap(uint8 id) external view returns (uint128) {
                        return _eModeCategories[id].collateralBitmap;
                      }
                      /// @inheritdoc IPool
                      function getEModeCategoryBorrowableBitmap(uint8 id) external view returns (uint128) {
                        return _eModeCategories[id].borrowableBitmap;
                      }
                      /// @inheritdoc IPool
                      function setUserEMode(uint8 categoryId) external virtual override {
                        EModeLogic.executeSetUserEMode(
                          _reserves,
                          _reservesList,
                          _eModeCategories,
                          _usersEModeCategory,
                          _usersConfig[msg.sender],
                          DataTypes.ExecuteSetUserEModeParams({
                            reservesCount: _reservesCount,
                            oracle: ADDRESSES_PROVIDER.getPriceOracle(),
                            categoryId: categoryId
                          })
                        );
                      }
                      /// @inheritdoc IPool
                      function getUserEMode(address user) external view virtual override returns (uint256) {
                        return _usersEModeCategory[user];
                      }
                      /// @inheritdoc IPool
                      function resetIsolationModeTotalDebt(
                        address asset
                      ) external virtual override onlyPoolConfigurator {
                        PoolLogic.executeResetIsolationModeTotalDebt(_reserves, asset);
                      }
                      /// @inheritdoc IPool
                      function getLiquidationGracePeriod(address asset) external virtual override returns (uint40) {
                        return _reserves[asset].liquidationGracePeriodUntil;
                      }
                      /// @inheritdoc IPool
                      function setLiquidationGracePeriod(
                        address asset,
                        uint40 until
                      ) external virtual override onlyPoolConfigurator {
                        require(_reserves[asset].id != 0 || _reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                        PoolLogic.executeSetLiquidationGracePeriod(_reserves, asset, until);
                      }
                      /// @inheritdoc IPool
                      function rescueTokens(
                        address token,
                        address to,
                        uint256 amount
                      ) external virtual override onlyPoolAdmin {
                        PoolLogic.executeRescueTokens(token, to, amount);
                      }
                      /// @inheritdoc IPool
                      /// @dev Deprecated: maintained for compatibility purposes
                      function deposit(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external virtual override {
                        SupplyLogic.executeSupply(
                          _reserves,
                          _reservesList,
                          _usersConfig[onBehalfOf],
                          DataTypes.ExecuteSupplyParams({
                            asset: asset,
                            amount: amount,
                            onBehalfOf: onBehalfOf,
                            referralCode: referralCode
                          })
                        );
                      }
                      /// @inheritdoc IPool
                      function getFlashLoanLogic() external pure returns (address) {
                        return address(FlashLoanLogic);
                      }
                      /// @inheritdoc IPool
                      function getBorrowLogic() external pure returns (address) {
                        return address(BorrowLogic);
                      }
                      /// @inheritdoc IPool
                      function getBridgeLogic() external pure returns (address) {
                        return address(BridgeLogic);
                      }
                      /// @inheritdoc IPool
                      function getEModeLogic() external pure returns (address) {
                        return address(EModeLogic);
                      }
                      /// @inheritdoc IPool
                      function getLiquidationLogic() external pure returns (address) {
                        return address(LiquidationLogic);
                      }
                      /// @inheritdoc IPool
                      function getPoolLogic() external pure returns (address) {
                        return address(PoolLogic);
                      }
                      /// @inheritdoc IPool
                      function getSupplyLogic() external pure returns (address) {
                        return address(SupplyLogic);
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IPoolAddressesProvider
                     * @author Aave
                     * @notice Defines the basic interface for a Pool Addresses Provider.
                     */
                    interface IPoolAddressesProvider {
                      /**
                       * @dev Emitted when the market identifier is updated.
                       * @param oldMarketId The old id of the market
                       * @param newMarketId The new id of the market
                       */
                      event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                      /**
                       * @dev Emitted when the pool is updated.
                       * @param oldAddress The old address of the Pool
                       * @param newAddress The new address of the Pool
                       */
                      event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool configurator is updated.
                       * @param oldAddress The old address of the PoolConfigurator
                       * @param newAddress The new address of the PoolConfigurator
                       */
                      event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle is updated.
                       * @param oldAddress The old address of the PriceOracle
                       * @param newAddress The new address of the PriceOracle
                       */
                      event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL manager is updated.
                       * @param oldAddress The old address of the ACLManager
                       * @param newAddress The new address of the ACLManager
                       */
                      event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL admin is updated.
                       * @param oldAddress The old address of the ACLAdmin
                       * @param newAddress The new address of the ACLAdmin
                       */
                      event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle sentinel is updated.
                       * @param oldAddress The old address of the PriceOracleSentinel
                       * @param newAddress The new address of the PriceOracleSentinel
                       */
                      event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool data provider is updated.
                       * @param oldAddress The old address of the PoolDataProvider
                       * @param newAddress The new address of the PoolDataProvider
                       */
                      event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when a new proxy is created.
                       * @param id The identifier of the proxy
                       * @param proxyAddress The address of the created proxy contract
                       * @param implementationAddress The address of the implementation contract
                       */
                      event ProxyCreated(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address indexed implementationAddress
                      );
                      /**
                       * @dev Emitted when a new non-proxied contract address is registered.
                       * @param id The identifier of the contract
                       * @param oldAddress The address of the old contract
                       * @param newAddress The address of the new contract
                       */
                      event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the implementation of the proxy registered with id is updated
                       * @param id The identifier of the contract
                       * @param proxyAddress The address of the proxy contract
                       * @param oldImplementationAddress The address of the old implementation contract
                       * @param newImplementationAddress The address of the new implementation contract
                       */
                      event AddressSetAsProxy(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address oldImplementationAddress,
                        address indexed newImplementationAddress
                      );
                      /**
                       * @notice Returns the id of the Aave market to which this contract points to.
                       * @return The market id
                       */
                      function getMarketId() external view returns (string memory);
                      /**
                       * @notice Associates an id with a specific PoolAddressesProvider.
                       * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                       * identify and validate multiple Aave markets.
                       * @param newMarketId The market id
                       */
                      function setMarketId(string calldata newMarketId) external;
                      /**
                       * @notice Returns an address by its identifier.
                       * @dev The returned address might be an EOA or a contract, potentially proxied
                       * @dev It returns ZERO if there is no registered address with the given id
                       * @param id The id
                       * @return The address of the registered for the specified id
                       */
                      function getAddress(bytes32 id) external view returns (address);
                      /**
                       * @notice 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 `newImplementationAddress`.
                       * @dev 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 newImplementationAddress The address of the new implementation
                       */
                      function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                      /**
                       * @notice Sets an address for an id replacing the address saved in the addresses map.
                       * @dev 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;
                      /**
                       * @notice Returns the address of the Pool proxy.
                       * @return The Pool proxy address
                       */
                      function getPool() external view returns (address);
                      /**
                       * @notice Updates the implementation of the Pool, or creates a proxy
                       * setting the new `pool` implementation when the function is called for the first time.
                       * @param newPoolImpl The new Pool implementation
                       */
                      function setPoolImpl(address newPoolImpl) external;
                      /**
                       * @notice Returns the address of the PoolConfigurator proxy.
                       * @return The PoolConfigurator proxy address
                       */
                      function getPoolConfigurator() external view returns (address);
                      /**
                       * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                       * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                       * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                       */
                      function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                      /**
                       * @notice Returns the address of the price oracle.
                       * @return The address of the PriceOracle
                       */
                      function getPriceOracle() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle.
                       * @param newPriceOracle The address of the new PriceOracle
                       */
                      function setPriceOracle(address newPriceOracle) external;
                      /**
                       * @notice Returns the address of the ACL manager.
                       * @return The address of the ACLManager
                       */
                      function getACLManager() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL manager.
                       * @param newAclManager The address of the new ACLManager
                       */
                      function setACLManager(address newAclManager) external;
                      /**
                       * @notice Returns the address of the ACL admin.
                       * @return The address of the ACL admin
                       */
                      function getACLAdmin() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL admin.
                       * @param newAclAdmin The address of the new ACL admin
                       */
                      function setACLAdmin(address newAclAdmin) external;
                      /**
                       * @notice Returns the address of the price oracle sentinel.
                       * @return The address of the PriceOracleSentinel
                       */
                      function getPriceOracleSentinel() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle sentinel.
                       * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                       */
                      function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                      /**
                       * @notice Returns the address of the data provider.
                       * @return The address of the DataProvider
                       */
                      function getPoolDataProvider() external view returns (address);
                      /**
                       * @notice Updates the address of the data provider.
                       * @param newDataProvider The address of the new DataProvider
                       */
                      function setPoolDataProvider(address newDataProvider) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title Errors library
                     * @author Aave
                     * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                     */
                    library Errors {
                      string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                      string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                      string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                      string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                      string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                      string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                      string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                      string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                      string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                      string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                      string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                      string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                      string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                      string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                      string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                      string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                      string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                      string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                      string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                      string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                      string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                      string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                      string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                      string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                      string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                      string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                      string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                      string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                      string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                      string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                      string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                      string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                      string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                      string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                      string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                      string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                      string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                      string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                      string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                      string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                      string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                      string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                      string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                      string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                      string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                      string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                      string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                      string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                      string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                      string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                      string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                      string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                      string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                      string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                      string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                      string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                      string public constant USER_IN_ISOLATION_MODE_OR_LTV_ZERO = '62'; // 'User is in isolation mode or ltv is zero'
                      string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                      string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                      string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                      string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                      string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                      string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                      string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                      string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                      string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                      string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                      string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                      string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                      string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                      string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                      string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                      string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                      string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                      string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                      string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                      string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                      string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                      string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                      string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                      string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                      string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                      string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                      string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                      string public constant INVALID_MAX_RATE = '92'; // The expect maximum borrow rate is invalid
                      string public constant WITHDRAW_TO_ATOKEN = '93'; // Withdrawing to the aToken is not allowed
                      string public constant SUPPLY_TO_ATOKEN = '94'; // Supplying to the aToken is not allowed
                      string public constant SLOPE_2_MUST_BE_GTE_SLOPE_1 = '95'; // Variable interest rate slope 2 can not be lower than slope 1
                      string public constant CALLER_NOT_RISK_OR_POOL_OR_EMERGENCY_ADMIN = '96'; // 'The caller of the function is not a risk, pool or emergency admin'
                      string public constant LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED = '97'; // 'Liquidation grace sentinel validation failed'
                      string public constant INVALID_GRACE_PERIOD = '98'; // Grace period above a valid range
                      string public constant INVALID_FREEZE_STATE = '99'; // Reserve is already in the passed freeze state
                      string public constant NOT_BORROWABLE_IN_EMODE = '100'; // Asset not borrowable in eMode
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.10;
                    /**
                     * @title VersionedInitializable
                     * @author Aave, inspired by the OpenZeppelin Initializable contract
                     * @notice Helper contract to implement initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * @dev 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.
                     */
                    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;
                        }
                      }
                      /**
                       * @notice Returns the revision number of the contract
                       * @dev Needs to be defined in the inherited class as a constant.
                       * @return The revision number
                       */
                      function getRevision() internal pure virtual returns (uint256);
                      /**
                       * @notice Returns true if and only if the function is running in the constructor
                       * @return True 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: MIT
                    pragma solidity ^0.8.0;
                    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 internal constant LTV_MASK =                       0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore
                      uint256 internal constant LIQUIDATION_THRESHOLD_MASK =     0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore
                      uint256 internal constant LIQUIDATION_BONUS_MASK =         0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore
                      uint256 internal constant DECIMALS_MASK =                  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant ACTIVE_MASK =                    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant FROZEN_MASK =                    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant BORROWING_MASK =                 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore
                      // @notice there is an unoccupied hole of 1 bit at position 59 from pre 3.2 stableBorrowRateEnabled
                      uint256 internal constant PAUSED_MASK =                    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant BORROWABLE_IN_ISOLATION_MASK =   0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant SILOED_BORROWING_MASK =          0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant FLASHLOAN_ENABLED_MASK =         0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant RESERVE_FACTOR_MASK =            0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant BORROW_CAP_MASK =                0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant SUPPLY_CAP_MASK =                0xFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant LIQUIDATION_PROTOCOL_FEE_MASK =  0xFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      // @notice there is an unoccupied hole of 8 bits from 168 to 176 left from pre 3.2 eModeCategory
                      uint256 internal constant UNBACKED_MINT_CAP_MASK =         0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant DEBT_CEILING_MASK =              0xF0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant VIRTUAL_ACC_ACTIVE_MASK =        0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed
                      uint256 internal constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16;
                      uint256 internal constant LIQUIDATION_BONUS_START_BIT_POSITION = 32;
                      uint256 internal constant RESERVE_DECIMALS_START_BIT_POSITION = 48;
                      uint256 internal constant IS_ACTIVE_START_BIT_POSITION = 56;
                      uint256 internal constant IS_FROZEN_START_BIT_POSITION = 57;
                      uint256 internal constant BORROWING_ENABLED_START_BIT_POSITION = 58;
                      uint256 internal constant IS_PAUSED_START_BIT_POSITION = 60;
                      uint256 internal constant BORROWABLE_IN_ISOLATION_START_BIT_POSITION = 61;
                      uint256 internal constant SILOED_BORROWING_START_BIT_POSITION = 62;
                      uint256 internal constant FLASHLOAN_ENABLED_START_BIT_POSITION = 63;
                      uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64;
                      uint256 internal constant BORROW_CAP_START_BIT_POSITION = 80;
                      uint256 internal constant SUPPLY_CAP_START_BIT_POSITION = 116;
                      uint256 internal constant LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION = 152;
                      //@notice there is an unoccupied hole of 8 bits from 168 to 176 left from pre 3.2 eModeCategory
                      uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176;
                      uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212;
                      uint256 internal constant VIRTUAL_ACC_START_BIT_POSITION = 252;
                      uint256 internal constant MAX_VALID_LTV = 65535;
                      uint256 internal constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535;
                      uint256 internal constant MAX_VALID_LIQUIDATION_BONUS = 65535;
                      uint256 internal constant MAX_VALID_DECIMALS = 255;
                      uint256 internal constant MAX_VALID_RESERVE_FACTOR = 65535;
                      uint256 internal constant MAX_VALID_BORROW_CAP = 68719476735;
                      uint256 internal constant MAX_VALID_SUPPLY_CAP = 68719476735;
                      uint256 internal constant MAX_VALID_LIQUIDATION_PROTOCOL_FEE = 65535;
                      uint256 internal constant MAX_VALID_UNBACKED_MINT_CAP = 68719476735;
                      uint256 internal constant MAX_VALID_DEBT_CEILING = 1099511627775;
                      uint256 public constant DEBT_CEILING_DECIMALS = 2;
                      uint16 public constant MAX_RESERVES_COUNT = 128;
                      /**
                       * @notice 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.INVALID_LTV);
                        self.data = (self.data & LTV_MASK) | ltv;
                      }
                      /**
                       * @notice Gets the Loan to Value of the reserve
                       * @param self The reserve configuration
                       * @return The loan to value
                       */
                      function getLtv(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) {
                        return self.data & ~LTV_MASK;
                      }
                      /**
                       * @notice 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.INVALID_LIQ_THRESHOLD);
                        self.data =
                          (self.data & LIQUIDATION_THRESHOLD_MASK) |
                          (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the liquidation threshold of the reserve
                       * @param self The reserve configuration
                       * @return The liquidation threshold
                       */
                      function getLiquidationThreshold(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION;
                      }
                      /**
                       * @notice 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.INVALID_LIQ_BONUS);
                        self.data =
                          (self.data & LIQUIDATION_BONUS_MASK) |
                          (bonus << LIQUIDATION_BONUS_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the liquidation bonus of the reserve
                       * @param self The reserve configuration
                       * @return The liquidation bonus
                       */
                      function getLiquidationBonus(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION;
                      }
                      /**
                       * @notice 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.INVALID_DECIMALS);
                        self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION);
                      }
                      /**
                       * @notice 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 memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
                      }
                      /**
                       * @notice 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);
                      }
                      /**
                       * @notice Gets the active state of the reserve
                       * @param self The reserve configuration
                       * @return The active state
                       */
                      function getActive(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                        return (self.data & ~ACTIVE_MASK) != 0;
                      }
                      /**
                       * @notice 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);
                      }
                      /**
                       * @notice Gets the frozen state of the reserve
                       * @param self The reserve configuration
                       * @return The frozen state
                       */
                      function getFrozen(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                        return (self.data & ~FROZEN_MASK) != 0;
                      }
                      /**
                       * @notice Sets the paused state of the reserve
                       * @param self The reserve configuration
                       * @param paused The paused state
                       */
                      function setPaused(DataTypes.ReserveConfigurationMap memory self, bool paused) internal pure {
                        self.data =
                          (self.data & PAUSED_MASK) |
                          (uint256(paused ? 1 : 0) << IS_PAUSED_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the paused state of the reserve
                       * @param self The reserve configuration
                       * @return The paused state
                       */
                      function getPaused(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                        return (self.data & ~PAUSED_MASK) != 0;
                      }
                      /**
                       * @notice Sets the borrowable in isolation flag for the reserve.
                       * @dev When this flag is set to true, the asset will be borrowable against isolated collaterals and the borrowed
                       * amount will be accumulated in the isolated collateral's total debt exposure.
                       * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
                       * consistency in the debt ceiling calculations.
                       * @param self The reserve configuration
                       * @param borrowable True if the asset is borrowable
                       */
                      function setBorrowableInIsolation(
                        DataTypes.ReserveConfigurationMap memory self,
                        bool borrowable
                      ) internal pure {
                        self.data =
                          (self.data & BORROWABLE_IN_ISOLATION_MASK) |
                          (uint256(borrowable ? 1 : 0) << BORROWABLE_IN_ISOLATION_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the borrowable in isolation flag for the reserve.
                       * @dev If the returned flag is true, the asset is borrowable against isolated collateral. Assets borrowed with
                       * isolated collateral is accounted for in the isolated collateral's total debt exposure.
                       * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
                       * consistency in the debt ceiling calculations.
                       * @param self The reserve configuration
                       * @return The borrowable in isolation flag
                       */
                      function getBorrowableInIsolation(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~BORROWABLE_IN_ISOLATION_MASK) != 0;
                      }
                      /**
                       * @notice Sets the siloed borrowing flag for the reserve.
                       * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
                       * @param self The reserve configuration
                       * @param siloed True if the asset is siloed
                       */
                      function setSiloedBorrowing(
                        DataTypes.ReserveConfigurationMap memory self,
                        bool siloed
                      ) internal pure {
                        self.data =
                          (self.data & SILOED_BORROWING_MASK) |
                          (uint256(siloed ? 1 : 0) << SILOED_BORROWING_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the siloed borrowing flag for the reserve.
                       * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
                       * @param self The reserve configuration
                       * @return The siloed borrowing flag
                       */
                      function getSiloedBorrowing(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~SILOED_BORROWING_MASK) != 0;
                      }
                      /**
                       * @notice 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);
                      }
                      /**
                       * @notice Gets the borrowing state of the reserve
                       * @param self The reserve configuration
                       * @return The borrowing state
                       */
                      function getBorrowingEnabled(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~BORROWING_MASK) != 0;
                      }
                      /**
                       * @notice 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.INVALID_RESERVE_FACTOR);
                        self.data =
                          (self.data & RESERVE_FACTOR_MASK) |
                          (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the reserve factor of the reserve
                       * @param self The reserve configuration
                       * @return The reserve factor
                       */
                      function getReserveFactor(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the borrow cap of the reserve
                       * @param self The reserve configuration
                       * @param borrowCap The borrow cap
                       */
                      function setBorrowCap(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 borrowCap
                      ) internal pure {
                        require(borrowCap <= MAX_VALID_BORROW_CAP, Errors.INVALID_BORROW_CAP);
                        self.data = (self.data & BORROW_CAP_MASK) | (borrowCap << BORROW_CAP_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the borrow cap of the reserve
                       * @param self The reserve configuration
                       * @return The borrow cap
                       */
                      function getBorrowCap(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the supply cap of the reserve
                       * @param self The reserve configuration
                       * @param supplyCap The supply cap
                       */
                      function setSupplyCap(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 supplyCap
                      ) internal pure {
                        require(supplyCap <= MAX_VALID_SUPPLY_CAP, Errors.INVALID_SUPPLY_CAP);
                        self.data = (self.data & SUPPLY_CAP_MASK) | (supplyCap << SUPPLY_CAP_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the supply cap of the reserve
                       * @param self The reserve configuration
                       * @return The supply cap
                       */
                      function getSupplyCap(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the debt ceiling in isolation mode for the asset
                       * @param self The reserve configuration
                       * @param ceiling The maximum debt ceiling for the asset
                       */
                      function setDebtCeiling(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 ceiling
                      ) internal pure {
                        require(ceiling <= MAX_VALID_DEBT_CEILING, Errors.INVALID_DEBT_CEILING);
                        self.data = (self.data & DEBT_CEILING_MASK) | (ceiling << DEBT_CEILING_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the debt ceiling for the asset if the asset is in isolation mode
                       * @param self The reserve configuration
                       * @return The debt ceiling (0 = isolation mode disabled)
                       */
                      function getDebtCeiling(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~DEBT_CEILING_MASK) >> DEBT_CEILING_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the liquidation protocol fee of the reserve
                       * @param self The reserve configuration
                       * @param liquidationProtocolFee The liquidation protocol fee
                       */
                      function setLiquidationProtocolFee(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 liquidationProtocolFee
                      ) internal pure {
                        require(
                          liquidationProtocolFee <= MAX_VALID_LIQUIDATION_PROTOCOL_FEE,
                          Errors.INVALID_LIQUIDATION_PROTOCOL_FEE
                        );
                        self.data =
                          (self.data & LIQUIDATION_PROTOCOL_FEE_MASK) |
                          (liquidationProtocolFee << LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION);
                      }
                      /**
                       * @dev Gets the liquidation protocol fee
                       * @param self The reserve configuration
                       * @return The liquidation protocol fee
                       */
                      function getLiquidationProtocolFee(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return
                          (self.data & ~LIQUIDATION_PROTOCOL_FEE_MASK) >> LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the unbacked mint cap of the reserve
                       * @param self The reserve configuration
                       * @param unbackedMintCap The unbacked mint cap
                       */
                      function setUnbackedMintCap(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 unbackedMintCap
                      ) internal pure {
                        require(unbackedMintCap <= MAX_VALID_UNBACKED_MINT_CAP, Errors.INVALID_UNBACKED_MINT_CAP);
                        self.data =
                          (self.data & UNBACKED_MINT_CAP_MASK) |
                          (unbackedMintCap << UNBACKED_MINT_CAP_START_BIT_POSITION);
                      }
                      /**
                       * @dev Gets the unbacked mint cap of the reserve
                       * @param self The reserve configuration
                       * @return The unbacked mint cap
                       */
                      function getUnbackedMintCap(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~UNBACKED_MINT_CAP_MASK) >> UNBACKED_MINT_CAP_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the flashloanable flag for the reserve
                       * @param self The reserve configuration
                       * @param flashLoanEnabled True if the asset is flashloanable, false otherwise
                       */
                      function setFlashLoanEnabled(
                        DataTypes.ReserveConfigurationMap memory self,
                        bool flashLoanEnabled
                      ) internal pure {
                        self.data =
                          (self.data & FLASHLOAN_ENABLED_MASK) |
                          (uint256(flashLoanEnabled ? 1 : 0) << FLASHLOAN_ENABLED_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the flashloanable flag for the reserve
                       * @param self The reserve configuration
                       * @return The flashloanable flag
                       */
                      function getFlashLoanEnabled(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~FLASHLOAN_ENABLED_MASK) != 0;
                      }
                      /**
                       * @notice Sets the virtual account active/not state of the reserve
                       * @param self The reserve configuration
                       * @param active The active state
                       */
                      function setVirtualAccActive(
                        DataTypes.ReserveConfigurationMap memory self,
                        bool active
                      ) internal pure {
                        self.data =
                          (self.data & VIRTUAL_ACC_ACTIVE_MASK) |
                          (uint256(active ? 1 : 0) << VIRTUAL_ACC_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the virtual account active/not state of the reserve
                       * @dev The state should be true for all normal assets and should be false
                       *  only in special cases (ex. GHO) where an asset is minted instead of supplied.
                       * @param self The reserve configuration
                       * @return The active state
                       */
                      function getIsVirtualAccActive(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~VIRTUAL_ACC_ACTIVE_MASK) != 0;
                      }
                      /**
                       * @notice Gets the configuration flags of the reserve
                       * @param self The reserve configuration
                       * @return The state flag representing active
                       * @return The state flag representing frozen
                       * @return The state flag representing borrowing enabled
                       * @return The state flag representing paused
                       */
                      function getFlags(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool, bool, bool, bool) {
                        uint256 dataLocal = self.data;
                        return (
                          (dataLocal & ~ACTIVE_MASK) != 0,
                          (dataLocal & ~FROZEN_MASK) != 0,
                          (dataLocal & ~BORROWING_MASK) != 0,
                          (dataLocal & ~PAUSED_MASK) != 0
                        );
                      }
                      /**
                       * @notice Gets the configuration parameters of the reserve from storage
                       * @param self The reserve configuration
                       * @return The state param representing ltv
                       * @return The state param representing liquidation threshold
                       * @return The state param representing liquidation bonus
                       * @return The state param representing reserve decimals
                       * @return The state param representing reserve factor
                       */
                      function getParams(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure 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
                        );
                      }
                      /**
                       * @notice Gets the caps parameters of the reserve from storage
                       * @param self The reserve configuration
                       * @return The state param representing borrow cap
                       * @return The state param representing supply cap.
                       */
                      function getCaps(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256, uint256) {
                        uint256 dataLocal = self.data;
                        return (
                          (dataLocal & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION,
                          (dataLocal & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION
                        );
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {Address} from '../../../dependencies/openzeppelin/contracts/Address.sol';
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {Errors} from '../helpers/Errors.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {GenericLogic} from './GenericLogic.sol';
                    /**
                     * @title PoolLogic library
                     * @author Aave
                     * @notice Implements the logic for Pool specific functions
                     */
                    library PoolLogic {
                      using GPv2SafeERC20 for IERC20;
                      using WadRayMath for uint256;
                      using ReserveLogic for DataTypes.ReserveData;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      // See `IPool` for descriptions
                      event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                      event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                      /**
                       * @notice Initialize an asset reserve and add the reserve to the list of reserves
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param params Additional parameters needed for initiation
                       * @return true if appended, false if inserted at existing empty spot
                       */
                      function executeInitReserve(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.InitReserveParams memory params
                      ) external returns (bool) {
                        require(Address.isContract(params.asset), Errors.NOT_CONTRACT);
                        reservesData[params.asset].init(
                          params.aTokenAddress,
                          params.variableDebtAddress,
                          params.interestRateStrategyAddress
                        );
                        bool reserveAlreadyAdded = reservesData[params.asset].id != 0 ||
                          reservesList[0] == params.asset;
                        require(!reserveAlreadyAdded, Errors.RESERVE_ALREADY_ADDED);
                        for (uint16 i = 0; i < params.reservesCount; i++) {
                          if (reservesList[i] == address(0)) {
                            reservesData[params.asset].id = i;
                            reservesList[i] = params.asset;
                            return false;
                          }
                        }
                        require(params.reservesCount < params.maxNumberReserves, Errors.NO_MORE_RESERVES_ALLOWED);
                        reservesData[params.asset].id = params.reservesCount;
                        reservesList[params.reservesCount] = params.asset;
                        return true;
                      }
                      /**
                       * @notice Rescue and transfer tokens locked in this contract
                       * @param token The address of the token
                       * @param to The address of the recipient
                       * @param amount The amount of token to transfer
                       */
                      function executeRescueTokens(address token, address to, uint256 amount) external {
                        IERC20(token).safeTransfer(to, amount);
                      }
                      /**
                       * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                       * @param reservesData The state of all the reserves
                       * @param assets The list of reserves for which the minting needs to be executed
                       */
                      function executeMintToTreasury(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        address[] calldata assets
                      ) external {
                        for (uint256 i = 0; i < assets.length; i++) {
                          address assetAddress = assets[i];
                          DataTypes.ReserveData storage reserve = reservesData[assetAddress];
                          // this cover both inactive reserves and invalid reserves since the flag will be 0 for both
                          if (!reserve.configuration.getActive()) {
                            continue;
                          }
                          uint256 accruedToTreasury = reserve.accruedToTreasury;
                          if (accruedToTreasury != 0) {
                            reserve.accruedToTreasury = 0;
                            uint256 normalizedIncome = reserve.getNormalizedIncome();
                            uint256 amountToMint = accruedToTreasury.rayMul(normalizedIncome);
                            IAToken(reserve.aTokenAddress).mintToTreasury(amountToMint, normalizedIncome);
                            emit MintedToTreasury(assetAddress, amountToMint);
                          }
                        }
                      }
                      /**
                       * @notice Resets the isolation mode total debt of the given asset to zero
                       * @dev It requires the given asset has zero debt ceiling
                       * @param reservesData The state of all the reserves
                       * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                       */
                      function executeResetIsolationModeTotalDebt(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        address asset
                      ) external {
                        require(reservesData[asset].configuration.getDebtCeiling() == 0, Errors.DEBT_CEILING_NOT_ZERO);
                        reservesData[asset].isolationModeTotalDebt = 0;
                        emit IsolationModeTotalDebtUpdated(asset, 0);
                      }
                      /**
                       * @notice Sets the liquidation grace period of the asset
                       * @param reservesData The state of all the reserves
                       * @param asset The address of the underlying asset to set the liquidationGracePeriod
                       * @param until Timestamp when the liquidation grace period will end
                       */
                      function executeSetLiquidationGracePeriod(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        address asset,
                        uint40 until
                      ) external {
                        reservesData[asset].liquidationGracePeriodUntil = until;
                      }
                      /**
                       * @notice Drop a reserve
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function executeDropReserve(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        address asset
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[asset];
                        ValidationLogic.validateDropReserve(reservesList, reserve, asset);
                        reservesList[reservesData[asset].id] = address(0);
                        delete reservesData[asset];
                      }
                      /**
                       * @notice Returns the user account data across all the reserves
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param params Additional params needed for the calculation
                       * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                       * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                       * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                       * @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 executeGetUserAccountData(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.CalculateUserAccountDataParams memory params
                      )
                        external
                        view
                        returns (
                          uint256 totalCollateralBase,
                          uint256 totalDebtBase,
                          uint256 availableBorrowsBase,
                          uint256 currentLiquidationThreshold,
                          uint256 ltv,
                          uint256 healthFactor
                        )
                      {
                        (
                          totalCollateralBase,
                          totalDebtBase,
                          ltv,
                          currentLiquidationThreshold,
                          healthFactor,
                        ) = GenericLogic.calculateUserAccountData(reservesData, reservesList, eModeCategories, params);
                        availableBorrowsBase = GenericLogic.calculateAvailableBorrows(
                          totalCollateralBase,
                          totalDebtBase,
                          ltv
                        );
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.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';
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    /**
                     * @title ReserveLogic library
                     * @author Aave
                     * @notice Implements the logic to update the reserves state
                     */
                    library ReserveLogic {
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using SafeCast for uint256;
                      using GPv2SafeERC20 for IERC20;
                      using ReserveLogic for DataTypes.ReserveData;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      // See `IPool` for descriptions
                      event ReserveDataUpdated(
                        address indexed reserve,
                        uint256 liquidityRate,
                        uint256 stableBorrowRate,
                        uint256 variableBorrowRate,
                        uint256 liquidityIndex,
                        uint256 variableBorrowIndex
                      );
                      /**
                       * @notice Returns the ongoing normalized income for the reserve.
                       * @dev A value of 1e27 means there is no income. As time passes, the income is accrued
                       * @dev 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 == block.timestamp) {
                          //if the index was updated in the same block, no need to perform any calculation
                          return reserve.liquidityIndex;
                        } else {
                          return
                            MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul(
                              reserve.liquidityIndex
                            );
                        }
                      }
                      /**
                       * @notice Returns the ongoing normalized variable debt for the reserve.
                       * @dev A value of 1e27 means there is no debt. As time passes, the debt is accrued
                       * @dev 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 == block.timestamp) {
                          //if the index was updated in the same block, no need to perform any calculation
                          return reserve.variableBorrowIndex;
                        } else {
                          return
                            MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul(
                              reserve.variableBorrowIndex
                            );
                        }
                      }
                      /**
                       * @notice Updates the liquidity cumulative index and the variable borrow index.
                       * @param reserve The reserve object
                       * @param reserveCache The caching layer for the reserve data
                       */
                      function updateState(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.ReserveCache memory reserveCache
                      ) internal {
                        // If time didn't pass since last stored timestamp, skip state update
                        //solium-disable-next-line
                        if (reserve.lastUpdateTimestamp == uint40(block.timestamp)) {
                          return;
                        }
                        _updateIndexes(reserve, reserveCache);
                        _accrueToTreasury(reserve, reserveCache);
                        //solium-disable-next-line
                        reserve.lastUpdateTimestamp = uint40(block.timestamp);
                      }
                      /**
                       * @notice 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 suppliers.
                       * @param reserve The reserve object
                       * @param totalLiquidity The total liquidity available in the reserve
                       * @param amount The amount to accumulate
                       * @return The next liquidity index of the reserve
                       */
                      function cumulateToLiquidityIndex(
                        DataTypes.ReserveData storage reserve,
                        uint256 totalLiquidity,
                        uint256 amount
                      ) internal returns (uint256) {
                        //next liquidity index is calculated this way: `((amount / totalLiquidity) + 1) * liquidityIndex`
                        //division `amount / totalLiquidity` done in ray for precision
                        uint256 result = (amount.wadToRay().rayDiv(totalLiquidity.wadToRay()) + WadRayMath.RAY).rayMul(
                          reserve.liquidityIndex
                        );
                        reserve.liquidityIndex = result.toUint128();
                        return result;
                      }
                      /**
                       * @notice Initializes a reserve.
                       * @param reserve The reserve object
                       * @param aTokenAddress The address of the overlying atoken contract
                       * @param variableDebtTokenAddress The address of the overlying variable debt token contract
                       * @param interestRateStrategyAddress The address of the interest rate strategy contract
                       */
                      function init(
                        DataTypes.ReserveData storage reserve,
                        address aTokenAddress,
                        address variableDebtTokenAddress,
                        address interestRateStrategyAddress
                      ) internal {
                        require(reserve.aTokenAddress == address(0), Errors.RESERVE_ALREADY_INITIALIZED);
                        reserve.liquidityIndex = uint128(WadRayMath.RAY);
                        reserve.variableBorrowIndex = uint128(WadRayMath.RAY);
                        reserve.aTokenAddress = aTokenAddress;
                        reserve.variableDebtTokenAddress = variableDebtTokenAddress;
                        reserve.interestRateStrategyAddress = interestRateStrategyAddress;
                      }
                      /**
                       * @notice Updates the reserve current variable borrow rate and the current liquidity rate.
                       * @param reserve The reserve reserve to be updated
                       * @param reserveCache The caching layer for the reserve data
                       * @param reserveAddress The address of the reserve to be updated
                       * @param liquidityAdded The amount of liquidity added to the protocol (supply or repay) in the previous action
                       * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow)
                       */
                      function updateInterestRatesAndVirtualBalance(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.ReserveCache memory reserveCache,
                        address reserveAddress,
                        uint256 liquidityAdded,
                        uint256 liquidityTaken
                      ) internal {
                        uint256 totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul(
                          reserveCache.nextVariableBorrowIndex
                        );
                        (uint256 nextLiquidityRate, uint256 nextVariableRate) = IReserveInterestRateStrategy(
                          reserve.interestRateStrategyAddress
                        ).calculateInterestRates(
                            DataTypes.CalculateInterestRatesParams({
                              unbacked: reserve.unbacked,
                              liquidityAdded: liquidityAdded,
                              liquidityTaken: liquidityTaken,
                              totalDebt: totalVariableDebt,
                              reserveFactor: reserveCache.reserveFactor,
                              reserve: reserveAddress,
                              usingVirtualBalance: reserve.configuration.getIsVirtualAccActive(),
                              virtualUnderlyingBalance: reserve.virtualUnderlyingBalance
                            })
                          );
                        reserve.currentLiquidityRate = nextLiquidityRate.toUint128();
                        reserve.currentVariableBorrowRate = nextVariableRate.toUint128();
                        // Only affect virtual balance if the reserve uses it
                        if (reserve.configuration.getIsVirtualAccActive()) {
                          if (liquidityAdded > 0) {
                            reserve.virtualUnderlyingBalance += liquidityAdded.toUint128();
                          }
                          if (liquidityTaken > 0) {
                            reserve.virtualUnderlyingBalance -= liquidityTaken.toUint128();
                          }
                        }
                        emit ReserveDataUpdated(
                          reserveAddress,
                          nextLiquidityRate,
                          0,
                          nextVariableRate,
                          reserveCache.nextLiquidityIndex,
                          reserveCache.nextVariableBorrowIndex
                        );
                      }
                      /**
                       * @notice Mints part of the repaid interest to the reserve treasury as a function of the reserve factor for the
                       * specific asset.
                       * @param reserve The reserve to be updated
                       * @param reserveCache The caching layer for the reserve data
                       */
                      function _accrueToTreasury(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.ReserveCache memory reserveCache
                      ) internal {
                        if (reserveCache.reserveFactor == 0) {
                          return;
                        }
                        //calculate the total variable debt at moment of the last interaction
                        uint256 prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                          reserveCache.currVariableBorrowIndex
                        );
                        //calculate the new total variable debt after accumulation of the interest on the index
                        uint256 currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                          reserveCache.nextVariableBorrowIndex
                        );
                        //debt accrued is the sum of the current debt minus the sum of the debt at the last update
                        uint256 totalDebtAccrued = currTotalVariableDebt - prevTotalVariableDebt;
                        uint256 amountToMint = totalDebtAccrued.percentMul(reserveCache.reserveFactor);
                        if (amountToMint != 0) {
                          reserve.accruedToTreasury += amountToMint.rayDiv(reserveCache.nextLiquidityIndex).toUint128();
                        }
                      }
                      /**
                       * @notice Updates the reserve indexes and the timestamp of the update.
                       * @param reserve The reserve reserve to be updated
                       * @param reserveCache The cache layer holding the cached protocol data
                       */
                      function _updateIndexes(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.ReserveCache memory reserveCache
                      ) internal {
                        // Only cumulating on the supply side if there is any income being produced
                        // The case of Reserve Factor 100% is not a problem (currentLiquidityRate == 0),
                        // as liquidity index should not be updated
                        if (reserveCache.currLiquidityRate != 0) {
                          uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(
                            reserveCache.currLiquidityRate,
                            reserveCache.reserveLastUpdateTimestamp
                          );
                          reserveCache.nextLiquidityIndex = cumulatedLiquidityInterest.rayMul(
                            reserveCache.currLiquidityIndex
                          );
                          reserve.liquidityIndex = reserveCache.nextLiquidityIndex.toUint128();
                        }
                        // Variable borrow index only gets updated if there is any variable debt.
                        // reserveCache.currVariableBorrowRate != 0 is not a correct validation,
                        // because a positive base variable rate can be stored on
                        // reserveCache.currVariableBorrowRate, but the index should not increase
                        if (reserveCache.currScaledVariableDebt != 0) {
                          uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(
                            reserveCache.currVariableBorrowRate,
                            reserveCache.reserveLastUpdateTimestamp
                          );
                          reserveCache.nextVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(
                            reserveCache.currVariableBorrowIndex
                          );
                          reserve.variableBorrowIndex = reserveCache.nextVariableBorrowIndex.toUint128();
                        }
                      }
                      /**
                       * @notice Creates a cache object to avoid repeated storage reads and external contract calls when updating state and
                       * interest rates.
                       * @param reserve The reserve object for which the cache will be filled
                       * @return The cache object
                       */
                      function cache(
                        DataTypes.ReserveData storage reserve
                      ) internal view returns (DataTypes.ReserveCache memory) {
                        DataTypes.ReserveCache memory reserveCache;
                        reserveCache.reserveConfiguration = reserve.configuration;
                        reserveCache.reserveFactor = reserveCache.reserveConfiguration.getReserveFactor();
                        reserveCache.currLiquidityIndex = reserveCache.nextLiquidityIndex = reserve.liquidityIndex;
                        reserveCache.currVariableBorrowIndex = reserveCache.nextVariableBorrowIndex = reserve
                          .variableBorrowIndex;
                        reserveCache.currLiquidityRate = reserve.currentLiquidityRate;
                        reserveCache.currVariableBorrowRate = reserve.currentVariableBorrowRate;
                        reserveCache.aTokenAddress = reserve.aTokenAddress;
                        reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
                        reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp;
                        reserveCache.currScaledVariableDebt = reserveCache.nextScaledVariableDebt = IVariableDebtToken(
                          reserveCache.variableDebtTokenAddress
                        ).scaledTotalSupply();
                        return reserveCache;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    /**
                     * @title EModeLogic library
                     * @author Aave
                     * @notice Implements the base logic for all the actions related to the eMode
                     */
                    library EModeLogic {
                      using ReserveLogic for DataTypes.ReserveCache;
                      using ReserveLogic for DataTypes.ReserveData;
                      using GPv2SafeERC20 for IERC20;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      // See `IPool` for descriptions
                      event UserEModeSet(address indexed user, uint8 categoryId);
                      /**
                       * @notice Updates the user efficiency mode category
                       * @dev Will revert if user is borrowing non-compatible asset or change will drop HF < HEALTH_FACTOR_LIQUIDATION_THRESHOLD
                       * @dev Emits the `UserEModeSet` event
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param usersEModeCategory The state of all users efficiency mode category
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the setUserEMode function
                       */
                      function executeSetUserEMode(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        mapping(address => uint8) storage usersEModeCategory,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ExecuteSetUserEModeParams memory params
                      ) external {
                        if (usersEModeCategory[msg.sender] == params.categoryId) return;
                        ValidationLogic.validateSetUserEMode(
                          eModeCategories,
                          userConfig,
                          params.reservesCount,
                          params.categoryId
                        );
                        usersEModeCategory[msg.sender] = params.categoryId;
                        ValidationLogic.validateHealthFactor(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          userConfig,
                          msg.sender,
                          params.categoryId,
                          params.reservesCount,
                          params.oracle
                        );
                        emit UserEModeSet(msg.sender, params.categoryId);
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {Errors} from '../helpers/Errors.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    /**
                     * @title SupplyLogic library
                     * @author Aave
                     * @notice Implements the base logic for supply/withdraw
                     */
                    library SupplyLogic {
                      using ReserveLogic for DataTypes.ReserveCache;
                      using ReserveLogic for DataTypes.ReserveData;
                      using GPv2SafeERC20 for IERC20;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      // See `IPool` for descriptions
                      event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                      event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                      event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                      event Supply(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @notice Implements the supply feature. Through `supply()`, users supply assets to the Aave protocol.
                       * @dev Emits the `Supply()` event.
                       * @dev In the first supply action, `ReserveUsedAsCollateralEnabled()` is emitted, if the asset can be enabled as
                       * collateral.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the supply function
                       */
                      function executeSupply(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ExecuteSupplyParams memory params
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[params.asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        reserve.updateState(reserveCache);
                        ValidationLogic.validateSupply(reserveCache, reserve, params.amount, params.onBehalfOf);
                        reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, params.amount, 0);
                        IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, params.amount);
                        bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
                          msg.sender,
                          params.onBehalfOf,
                          params.amount,
                          reserveCache.nextLiquidityIndex
                        );
                        if (isFirstSupply) {
                          if (
                            ValidationLogic.validateAutomaticUseAsCollateral(
                              reservesData,
                              reservesList,
                              userConfig,
                              reserveCache.reserveConfiguration,
                              reserveCache.aTokenAddress
                            )
                          ) {
                            userConfig.setUsingAsCollateral(reserve.id, true);
                            emit ReserveUsedAsCollateralEnabled(params.asset, params.onBehalfOf);
                          }
                        }
                        emit Supply(params.asset, msg.sender, params.onBehalfOf, params.amount, params.referralCode);
                      }
                      /**
                       * @notice Implements the withdraw feature. Through `withdraw()`, users redeem their aTokens for the underlying asset
                       * previously supplied in the Aave protocol.
                       * @dev Emits the `Withdraw()` event.
                       * @dev If the user withdraws everything, `ReserveUsedAsCollateralDisabled()` is emitted.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the withdraw function
                       * @return The actual amount withdrawn
                       */
                      function executeWithdraw(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ExecuteWithdrawParams memory params
                      ) external returns (uint256) {
                        DataTypes.ReserveData storage reserve = reservesData[params.asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        require(params.to != reserveCache.aTokenAddress, Errors.WITHDRAW_TO_ATOKEN);
                        reserve.updateState(reserveCache);
                        uint256 userBalance = IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul(
                          reserveCache.nextLiquidityIndex
                        );
                        uint256 amountToWithdraw = params.amount;
                        if (params.amount == type(uint256).max) {
                          amountToWithdraw = userBalance;
                        }
                        ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance);
                        reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, 0, amountToWithdraw);
                        bool isCollateral = userConfig.isUsingAsCollateral(reserve.id);
                        if (isCollateral && amountToWithdraw == userBalance) {
                          userConfig.setUsingAsCollateral(reserve.id, false);
                          emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
                        }
                        IAToken(reserveCache.aTokenAddress).burn(
                          msg.sender,
                          params.to,
                          amountToWithdraw,
                          reserveCache.nextLiquidityIndex
                        );
                        if (isCollateral && userConfig.isBorrowingAny()) {
                          ValidationLogic.validateHFAndLtv(
                            reservesData,
                            reservesList,
                            eModeCategories,
                            userConfig,
                            params.asset,
                            msg.sender,
                            params.reservesCount,
                            params.oracle,
                            params.userEModeCategory
                          );
                        }
                        emit Withdraw(params.asset, msg.sender, params.to, amountToWithdraw);
                        return amountToWithdraw;
                      }
                      /**
                       * @notice Validates a transfer of aTokens. The sender is subjected to health factor validation to avoid
                       * collateralization constraints violation.
                       * @dev Emits the `ReserveUsedAsCollateralEnabled()` event for the `to` account, if the asset is being activated as
                       * collateral.
                       * @dev In case the `from` user transfers everything, `ReserveUsedAsCollateralDisabled()` is emitted for `from`.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the finalizeTransfer function
                       */
                      function executeFinalizeTransfer(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
                        DataTypes.FinalizeTransferParams memory params
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[params.asset];
                        ValidationLogic.validateTransfer(reserve);
                        uint256 reserveId = reserve.id;
                        uint256 scaledAmount = params.amount.rayDiv(reserve.getNormalizedIncome());
                        if (params.from != params.to && scaledAmount != 0) {
                          DataTypes.UserConfigurationMap storage fromConfig = usersConfig[params.from];
                          if (fromConfig.isUsingAsCollateral(reserveId)) {
                            if (fromConfig.isBorrowingAny()) {
                              ValidationLogic.validateHFAndLtv(
                                reservesData,
                                reservesList,
                                eModeCategories,
                                usersConfig[params.from],
                                params.asset,
                                params.from,
                                params.reservesCount,
                                params.oracle,
                                params.fromEModeCategory
                              );
                            }
                            if (params.balanceFromBefore == params.amount) {
                              fromConfig.setUsingAsCollateral(reserveId, false);
                              emit ReserveUsedAsCollateralDisabled(params.asset, params.from);
                            }
                          }
                          if (params.balanceToBefore == 0) {
                            DataTypes.UserConfigurationMap storage toConfig = usersConfig[params.to];
                            if (
                              ValidationLogic.validateAutomaticUseAsCollateral(
                                reservesData,
                                reservesList,
                                toConfig,
                                reserve.configuration,
                                reserve.aTokenAddress
                              )
                            ) {
                              toConfig.setUsingAsCollateral(reserveId, true);
                              emit ReserveUsedAsCollateralEnabled(params.asset, params.to);
                            }
                          }
                        }
                      }
                      /**
                       * @notice Executes the 'set as collateral' feature. A user can choose to activate or deactivate an asset as
                       * collateral at any point in time. Deactivating an asset as collateral is subjected to the usual health factor
                       * checks to ensure collateralization.
                       * @dev Emits the `ReserveUsedAsCollateralEnabled()` event if the asset can be activated as collateral.
                       * @dev In case the asset is being deactivated as collateral, `ReserveUsedAsCollateralDisabled()` is emitted.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The users configuration mapping that track the supplied/borrowed assets
                       * @param asset The address of the asset being configured as collateral
                       * @param useAsCollateral True if the user wants to set the asset as collateral, false otherwise
                       * @param reservesCount The number of initialized reserves
                       * @param priceOracle The address of the price oracle
                       * @param userEModeCategory The eMode category chosen by the user
                       */
                      function executeUseReserveAsCollateral(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap storage userConfig,
                        address asset,
                        bool useAsCollateral,
                        uint256 reservesCount,
                        address priceOracle,
                        uint8 userEModeCategory
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender);
                        ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance);
                        if (useAsCollateral == userConfig.isUsingAsCollateral(reserve.id)) return;
                        if (useAsCollateral) {
                          require(
                            ValidationLogic.validateUseAsCollateral(
                              reservesData,
                              reservesList,
                              userConfig,
                              reserveCache.reserveConfiguration
                            ),
                            Errors.USER_IN_ISOLATION_MODE_OR_LTV_ZERO
                          );
                          userConfig.setUsingAsCollateral(reserve.id, true);
                          emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
                        } else {
                          userConfig.setUsingAsCollateral(reserve.id, false);
                          ValidationLogic.validateHFAndLtv(
                            reservesData,
                            reservesList,
                            eModeCategories,
                            userConfig,
                            asset,
                            msg.sender,
                            reservesCount,
                            priceOracle,
                            userEModeCategory
                          );
                          emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IFlashLoanReceiver} from '../../../misc/flashloan/interfaces/IFlashLoanReceiver.sol';
                    import {IFlashLoanSimpleReceiver} from '../../../misc/flashloan/interfaces/IFlashLoanSimpleReceiver.sol';
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {Errors} from '../helpers/Errors.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {BorrowLogic} from './BorrowLogic.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    /**
                     * @title FlashLoanLogic library
                     * @author Aave
                     * @notice Implements the logic for the flash loans
                     */
                    library FlashLoanLogic {
                      using ReserveLogic for DataTypes.ReserveCache;
                      using ReserveLogic for DataTypes.ReserveData;
                      using GPv2SafeERC20 for IERC20;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using SafeCast for uint256;
                      // See `IPool` for descriptions
                      event FlashLoan(
                        address indexed target,
                        address initiator,
                        address indexed asset,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 premium,
                        uint16 indexed referralCode
                      );
                      // Helper struct for internal variables used in the `executeFlashLoan` function
                      struct FlashLoanLocalVars {
                        IFlashLoanReceiver receiver;
                        address currentAsset;
                        uint256 currentAmount;
                        uint256[] totalPremiums;
                        uint256 flashloanPremiumTotal;
                        uint256 flashloanPremiumToProtocol;
                      }
                      /**
                       * @notice Implements the flashloan feature that allow users to access liquidity of the pool for one transaction
                       * as long as the amount taken plus fee is returned or debt is opened.
                       * @dev For authorized flashborrowers the fee is waived
                       * @dev At the end of the transaction the pool will pull amount borrowed + fee from the receiver,
                       * if the receiver have not approved the pool the transaction will revert.
                       * @dev Emits the `FlashLoan()` event
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the flashloan function
                       */
                      function executeFlashLoan(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.FlashloanParams memory params
                      ) external {
                        // The usual action flow (cache -> updateState -> validation -> changeState -> updateRates)
                        // is altered to (validation -> user payload -> cache -> updateState -> changeState -> updateRates) for flashloans.
                        // This is done to protect against reentrance and rate manipulation within the user specified payload.
                        ValidationLogic.validateFlashloan(reservesData, params.assets, params.amounts);
                        FlashLoanLocalVars memory vars;
                        vars.totalPremiums = new uint256[](params.assets.length);
                        vars.receiver = IFlashLoanReceiver(params.receiverAddress);
                        (vars.flashloanPremiumTotal, vars.flashloanPremiumToProtocol) = params.isAuthorizedFlashBorrower
                          ? (0, 0)
                          : (params.flashLoanPremiumTotal, params.flashLoanPremiumToProtocol);
                        for (uint256 i = 0; i < params.assets.length; i++) {
                          vars.currentAmount = params.amounts[i];
                          vars.totalPremiums[i] = DataTypes.InterestRateMode(params.interestRateModes[i]) ==
                            DataTypes.InterestRateMode.NONE
                            ? vars.currentAmount.percentMul(vars.flashloanPremiumTotal)
                            : 0;
                          if (reservesData[params.assets[i]].configuration.getIsVirtualAccActive()) {
                            reservesData[params.assets[i]].virtualUnderlyingBalance -= vars.currentAmount.toUint128();
                          }
                          IAToken(reservesData[params.assets[i]].aTokenAddress).transferUnderlyingTo(
                            params.receiverAddress,
                            vars.currentAmount
                          );
                        }
                        require(
                          vars.receiver.executeOperation(
                            params.assets,
                            params.amounts,
                            vars.totalPremiums,
                            msg.sender,
                            params.params
                          ),
                          Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN
                        );
                        for (uint256 i = 0; i < params.assets.length; i++) {
                          vars.currentAsset = params.assets[i];
                          vars.currentAmount = params.amounts[i];
                          if (
                            DataTypes.InterestRateMode(params.interestRateModes[i]) == DataTypes.InterestRateMode.NONE
                          ) {
                            _handleFlashLoanRepayment(
                              reservesData[vars.currentAsset],
                              DataTypes.FlashLoanRepaymentParams({
                                asset: vars.currentAsset,
                                receiverAddress: params.receiverAddress,
                                amount: vars.currentAmount,
                                totalPremium: vars.totalPremiums[i],
                                flashLoanPremiumToProtocol: vars.flashloanPremiumToProtocol,
                                referralCode: params.referralCode
                              })
                            );
                          } else {
                            // If the user chose to not return the funds, the system checks if there is enough collateral and
                            // eventually opens a debt position
                            BorrowLogic.executeBorrow(
                              reservesData,
                              reservesList,
                              eModeCategories,
                              userConfig,
                              DataTypes.ExecuteBorrowParams({
                                asset: vars.currentAsset,
                                user: msg.sender,
                                onBehalfOf: params.onBehalfOf,
                                amount: vars.currentAmount,
                                interestRateMode: DataTypes.InterestRateMode(params.interestRateModes[i]),
                                referralCode: params.referralCode,
                                releaseUnderlying: false,
                                reservesCount: IPool(params.pool).getReservesCount(),
                                oracle: IPoolAddressesProvider(params.addressesProvider).getPriceOracle(),
                                userEModeCategory: IPool(params.pool).getUserEMode(params.onBehalfOf).toUint8(),
                                priceOracleSentinel: IPoolAddressesProvider(params.addressesProvider)
                                  .getPriceOracleSentinel()
                              })
                            );
                            // no premium is paid when taking on the flashloan as debt
                            emit FlashLoan(
                              params.receiverAddress,
                              msg.sender,
                              vars.currentAsset,
                              vars.currentAmount,
                              DataTypes.InterestRateMode(params.interestRateModes[i]),
                              0,
                              params.referralCode
                            );
                          }
                        }
                      }
                      /**
                       * @notice Implements the simple flashloan feature that allow users to access liquidity of ONE reserve for one
                       * transaction as long as the amount taken plus fee is returned.
                       * @dev Does not waive fee for approved flashborrowers nor allow taking on debt instead of repaying to save gas
                       * @dev At the end of the transaction the pool will pull amount borrowed + fee from the receiver,
                       * if the receiver have not approved the pool the transaction will revert.
                       * @dev Emits the `FlashLoan()` event
                       * @param reserve The state of the flashloaned reserve
                       * @param params The additional parameters needed to execute the simple flashloan function
                       */
                      function executeFlashLoanSimple(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.FlashloanSimpleParams memory params
                      ) external {
                        // The usual action flow (cache -> updateState -> validation -> changeState -> updateRates)
                        // is altered to (validation -> user payload -> cache -> updateState -> changeState -> updateRates) for flashloans.
                        // This is done to protect against reentrance and rate manipulation within the user specified payload.
                        ValidationLogic.validateFlashloanSimple(reserve, params.amount);
                        IFlashLoanSimpleReceiver receiver = IFlashLoanSimpleReceiver(params.receiverAddress);
                        uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal);
                        if (reserve.configuration.getIsVirtualAccActive()) {
                          reserve.virtualUnderlyingBalance -= params.amount.toUint128();
                        }
                        IAToken(reserve.aTokenAddress).transferUnderlyingTo(params.receiverAddress, params.amount);
                        require(
                          receiver.executeOperation(
                            params.asset,
                            params.amount,
                            totalPremium,
                            msg.sender,
                            params.params
                          ),
                          Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN
                        );
                        _handleFlashLoanRepayment(
                          reserve,
                          DataTypes.FlashLoanRepaymentParams({
                            asset: params.asset,
                            receiverAddress: params.receiverAddress,
                            amount: params.amount,
                            totalPremium: totalPremium,
                            flashLoanPremiumToProtocol: params.flashLoanPremiumToProtocol,
                            referralCode: params.referralCode
                          })
                        );
                      }
                      /**
                       * @notice Handles repayment of flashloaned assets + premium
                       * @dev Will pull the amount + premium from the receiver, so must have approved pool
                       * @param reserve The state of the flashloaned reserve
                       * @param params The additional parameters needed to execute the repayment function
                       */
                      function _handleFlashLoanRepayment(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.FlashLoanRepaymentParams memory params
                      ) internal {
                        uint256 premiumToProtocol = params.totalPremium.percentMul(params.flashLoanPremiumToProtocol);
                        uint256 premiumToLP = params.totalPremium - premiumToProtocol;
                        uint256 amountPlusPremium = params.amount + params.totalPremium;
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        reserve.updateState(reserveCache);
                        reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
                          IERC20(reserveCache.aTokenAddress).totalSupply() +
                            uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
                          premiumToLP
                        );
                        reserve.accruedToTreasury += premiumToProtocol
                          .rayDiv(reserveCache.nextLiquidityIndex)
                          .toUint128();
                        reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, amountPlusPremium, 0);
                        IERC20(params.asset).safeTransferFrom(
                          params.receiverAddress,
                          reserveCache.aTokenAddress,
                          amountPlusPremium
                        );
                        IAToken(reserveCache.aTokenAddress).handleRepayment(
                          params.receiverAddress,
                          params.receiverAddress,
                          amountPlusPremium
                        );
                        emit FlashLoan(
                          params.receiverAddress,
                          msg.sender,
                          params.asset,
                          params.amount,
                          DataTypes.InterestRateMode.NONE,
                          params.totalPremium,
                          params.referralCode
                        );
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {IsolationModeLogic} from './IsolationModeLogic.sol';
                    /**
                     * @title BorrowLogic library
                     * @author Aave
                     * @notice Implements the base logic for all the actions related to borrowing
                     */
                    library BorrowLogic {
                      using ReserveLogic for DataTypes.ReserveCache;
                      using ReserveLogic for DataTypes.ReserveData;
                      using GPv2SafeERC20 for IERC20;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using SafeCast for uint256;
                      // See `IPool` for descriptions
                      event Borrow(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 borrowRate,
                        uint16 indexed referralCode
                      );
                      event Repay(
                        address indexed reserve,
                        address indexed user,
                        address indexed repayer,
                        uint256 amount,
                        bool useATokens
                      );
                      event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                      event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                      /**
                       * @notice Implements the borrow feature. Borrowing allows users that provided collateral to draw liquidity from the
                       * Aave protocol proportionally to their collateralization power. For isolated positions, it also increases the
                       * isolated debt.
                       * @dev  Emits the `Borrow()` event
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the borrow function
                       */
                      function executeBorrow(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ExecuteBorrowParams memory params
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[params.asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        reserve.updateState(reserveCache);
                        (
                          bool isolationModeActive,
                          address isolationModeCollateralAddress,
                          uint256 isolationModeDebtCeiling
                        ) = userConfig.getIsolationModeState(reservesData, reservesList);
                        ValidationLogic.validateBorrow(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          DataTypes.ValidateBorrowParams({
                            reserveCache: reserveCache,
                            userConfig: userConfig,
                            asset: params.asset,
                            userAddress: params.onBehalfOf,
                            amount: params.amount,
                            interestRateMode: params.interestRateMode,
                            reservesCount: params.reservesCount,
                            oracle: params.oracle,
                            userEModeCategory: params.userEModeCategory,
                            priceOracleSentinel: params.priceOracleSentinel,
                            isolationModeActive: isolationModeActive,
                            isolationModeCollateralAddress: isolationModeCollateralAddress,
                            isolationModeDebtCeiling: isolationModeDebtCeiling
                          })
                        );
                        bool isFirstBorrowing = false;
                        (isFirstBorrowing, reserveCache.nextScaledVariableDebt) = IVariableDebtToken(
                          reserveCache.variableDebtTokenAddress
                        ).mint(params.user, params.onBehalfOf, params.amount, reserveCache.nextVariableBorrowIndex);
                        if (isFirstBorrowing) {
                          userConfig.setBorrowing(reserve.id, true);
                        }
                        if (isolationModeActive) {
                          uint256 nextIsolationModeTotalDebt = reservesData[isolationModeCollateralAddress]
                            .isolationModeTotalDebt += (params.amount /
                            10 **
                              (reserveCache.reserveConfiguration.getDecimals() -
                                ReserveConfiguration.DEBT_CEILING_DECIMALS)).toUint128();
                          emit IsolationModeTotalDebtUpdated(
                            isolationModeCollateralAddress,
                            nextIsolationModeTotalDebt
                          );
                        }
                        reserve.updateInterestRatesAndVirtualBalance(
                          reserveCache,
                          params.asset,
                          0,
                          params.releaseUnderlying ? params.amount : 0
                        );
                        if (params.releaseUnderlying) {
                          IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(params.user, params.amount);
                        }
                        emit Borrow(
                          params.asset,
                          params.user,
                          params.onBehalfOf,
                          params.amount,
                          DataTypes.InterestRateMode.VARIABLE,
                          reserve.currentVariableBorrowRate,
                          params.referralCode
                        );
                      }
                      /**
                       * @notice Implements the repay feature. Repaying transfers the underlying back to the aToken and clears the
                       * equivalent amount of debt for the user by burning the corresponding debt token. For isolated positions, it also
                       * reduces the isolated debt.
                       * @dev  Emits the `Repay()` event
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the repay function
                       * @return The actual amount being repaid
                       */
                      function executeRepay(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ExecuteRepayParams memory params
                      ) external returns (uint256) {
                        DataTypes.ReserveData storage reserve = reservesData[params.asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        reserve.updateState(reserveCache);
                        uint256 variableDebt = IERC20(reserveCache.variableDebtTokenAddress).balanceOf(
                          params.onBehalfOf
                        );
                        ValidationLogic.validateRepay(
                          reserveCache,
                          params.amount,
                          params.interestRateMode,
                          params.onBehalfOf,
                          variableDebt
                        );
                        uint256 paybackAmount = variableDebt;
                        // Allows a user to repay with aTokens without leaving dust from interest.
                        if (params.useATokens && params.amount == type(uint256).max) {
                          params.amount = IAToken(reserveCache.aTokenAddress).balanceOf(msg.sender);
                        }
                        if (params.amount < paybackAmount) {
                          paybackAmount = params.amount;
                        }
                        reserveCache.nextScaledVariableDebt = IVariableDebtToken(reserveCache.variableDebtTokenAddress)
                          .burn(params.onBehalfOf, paybackAmount, reserveCache.nextVariableBorrowIndex);
                        reserve.updateInterestRatesAndVirtualBalance(
                          reserveCache,
                          params.asset,
                          params.useATokens ? 0 : paybackAmount,
                          0
                        );
                        if (variableDebt - paybackAmount == 0) {
                          userConfig.setBorrowing(reserve.id, false);
                        }
                        IsolationModeLogic.updateIsolatedDebtIfIsolated(
                          reservesData,
                          reservesList,
                          userConfig,
                          reserveCache,
                          paybackAmount
                        );
                        if (params.useATokens) {
                          IAToken(reserveCache.aTokenAddress).burn(
                            msg.sender,
                            reserveCache.aTokenAddress,
                            paybackAmount,
                            reserveCache.nextLiquidityIndex
                          );
                          // in case of aToken repayment the msg.sender must always repay on behalf of itself
                          if (IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender) == 0) {
                            userConfig.setUsingAsCollateral(reserve.id, false);
                            emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
                          }
                        } else {
                          IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount);
                          IAToken(reserveCache.aTokenAddress).handleRepayment(
                            msg.sender,
                            params.onBehalfOf,
                            paybackAmount
                          );
                        }
                        emit Repay(params.asset, params.onBehalfOf, msg.sender, paybackAmount, params.useATokens);
                        return paybackAmount;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts//IERC20.sol';
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {PercentageMath} from '../../libraries/math/PercentageMath.sol';
                    import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                    import {DataTypes} from '../../libraries/types/DataTypes.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {GenericLogic} from './GenericLogic.sol';
                    import {IsolationModeLogic} from './IsolationModeLogic.sol';
                    import {EModeLogic} from './EModeLogic.sol';
                    import {UserConfiguration} from '../../libraries/configuration/UserConfiguration.sol';
                    import {ReserveConfiguration} from '../../libraries/configuration/ReserveConfiguration.sol';
                    import {EModeConfiguration} from '../../libraries/configuration/EModeConfiguration.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
                    import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                    /**
                     * @title LiquidationLogic library
                     * @author Aave
                     * @notice Implements actions involving management of collateral in the protocol, the main one being the liquidations
                     */
                    library LiquidationLogic {
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using ReserveLogic for DataTypes.ReserveCache;
                      using ReserveLogic for DataTypes.ReserveData;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using GPv2SafeERC20 for IERC20;
                      // See `IPool` for descriptions
                      event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                      event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                      event LiquidationCall(
                        address indexed collateralAsset,
                        address indexed debtAsset,
                        address indexed user,
                        uint256 debtToCover,
                        uint256 liquidatedCollateralAmount,
                        address liquidator,
                        bool receiveAToken
                      );
                      /**
                       * @dev Default percentage of borrower's debt to be repaid in a liquidation.
                       * @dev Percentage applied when the users health factor is above `CLOSE_FACTOR_HF_THRESHOLD`
                       * Expressed in bps, a value of 0.5e4 results in 50.00%
                       */
                      uint256 internal constant DEFAULT_LIQUIDATION_CLOSE_FACTOR = 0.5e4;
                      /**
                       * @dev Maximum percentage of borrower's debt to be repaid in a liquidation
                       * @dev Percentage applied when the users health factor is below `CLOSE_FACTOR_HF_THRESHOLD`
                       * Expressed in bps, a value of 1e4 results in 100.00%
                       */
                      uint256 public constant MAX_LIQUIDATION_CLOSE_FACTOR = 1e4;
                      /**
                       * @dev This constant represents below which health factor value it is possible to liquidate
                       * an amount of debt corresponding to `MAX_LIQUIDATION_CLOSE_FACTOR`.
                       * A value of 0.95e18 results in 0.95
                       */
                      uint256 public constant CLOSE_FACTOR_HF_THRESHOLD = 0.95e18;
                      struct LiquidationCallLocalVars {
                        uint256 userCollateralBalance;
                        uint256 userTotalDebt;
                        uint256 actualDebtToLiquidate;
                        uint256 actualCollateralToLiquidate;
                        uint256 liquidationBonus;
                        uint256 healthFactor;
                        uint256 liquidationProtocolFeeAmount;
                        IAToken collateralAToken;
                        DataTypes.ReserveCache debtReserveCache;
                      }
                      /**
                       * @notice 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 proportional amount of the `collateralAsset` plus a bonus to cover market risk
                       * @dev Emits the `LiquidationCall()` event
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param params The additional parameters needed to execute the liquidation function
                       */
                      function executeLiquidationCall(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.ExecuteLiquidationCallParams memory params
                      ) external {
                        LiquidationCallLocalVars memory vars;
                        DataTypes.ReserveData storage collateralReserve = reservesData[params.collateralAsset];
                        DataTypes.ReserveData storage debtReserve = reservesData[params.debtAsset];
                        DataTypes.UserConfigurationMap storage userConfig = usersConfig[params.user];
                        vars.debtReserveCache = debtReserve.cache();
                        debtReserve.updateState(vars.debtReserveCache);
                        (, , , , vars.healthFactor, ) = GenericLogic.calculateUserAccountData(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          DataTypes.CalculateUserAccountDataParams({
                            userConfig: userConfig,
                            reservesCount: params.reservesCount,
                            user: params.user,
                            oracle: params.priceOracle,
                            userEModeCategory: params.userEModeCategory
                          })
                        );
                        (vars.userTotalDebt, vars.actualDebtToLiquidate) = _calculateDebt(
                          vars.debtReserveCache,
                          params,
                          vars.healthFactor
                        );
                        ValidationLogic.validateLiquidationCall(
                          userConfig,
                          collateralReserve,
                          debtReserve,
                          DataTypes.ValidateLiquidationCallParams({
                            debtReserveCache: vars.debtReserveCache,
                            totalDebt: vars.userTotalDebt,
                            healthFactor: vars.healthFactor,
                            priceOracleSentinel: params.priceOracleSentinel
                          })
                        );
                        vars.collateralAToken = IAToken(collateralReserve.aTokenAddress);
                        if (
                          params.userEModeCategory != 0 &&
                          EModeConfiguration.isReserveEnabledOnBitmap(
                            eModeCategories[params.userEModeCategory].collateralBitmap,
                            collateralReserve.id
                          )
                        ) {
                          vars.liquidationBonus = eModeCategories[params.userEModeCategory].liquidationBonus;
                        } else {
                          vars.liquidationBonus = collateralReserve.configuration.getLiquidationBonus();
                        }
                        vars.userCollateralBalance = vars.collateralAToken.balanceOf(params.user);
                        (
                          vars.actualCollateralToLiquidate,
                          vars.actualDebtToLiquidate,
                          vars.liquidationProtocolFeeAmount
                        ) = _calculateAvailableCollateralToLiquidate(
                          collateralReserve,
                          vars.debtReserveCache,
                          params.collateralAsset,
                          params.debtAsset,
                          vars.actualDebtToLiquidate,
                          vars.userCollateralBalance,
                          vars.liquidationBonus,
                          IPriceOracleGetter(params.priceOracle)
                        );
                        if (vars.userTotalDebt == vars.actualDebtToLiquidate) {
                          userConfig.setBorrowing(debtReserve.id, false);
                        }
                        // If the collateral being liquidated is equal to the user balance,
                        // we set the currency as not being used as collateral anymore
                        if (
                          vars.actualCollateralToLiquidate + vars.liquidationProtocolFeeAmount ==
                          vars.userCollateralBalance
                        ) {
                          userConfig.setUsingAsCollateral(collateralReserve.id, false);
                          emit ReserveUsedAsCollateralDisabled(params.collateralAsset, params.user);
                        }
                        _burnDebtTokens(params, vars);
                        debtReserve.updateInterestRatesAndVirtualBalance(
                          vars.debtReserveCache,
                          params.debtAsset,
                          vars.actualDebtToLiquidate,
                          0
                        );
                        IsolationModeLogic.updateIsolatedDebtIfIsolated(
                          reservesData,
                          reservesList,
                          userConfig,
                          vars.debtReserveCache,
                          vars.actualDebtToLiquidate
                        );
                        if (params.receiveAToken) {
                          _liquidateATokens(reservesData, reservesList, usersConfig, collateralReserve, params, vars);
                        } else {
                          _burnCollateralATokens(collateralReserve, params, vars);
                        }
                        // Transfer fee to treasury if it is non-zero
                        if (vars.liquidationProtocolFeeAmount != 0) {
                          uint256 liquidityIndex = collateralReserve.getNormalizedIncome();
                          uint256 scaledDownLiquidationProtocolFee = vars.liquidationProtocolFeeAmount.rayDiv(
                            liquidityIndex
                          );
                          uint256 scaledDownUserBalance = vars.collateralAToken.scaledBalanceOf(params.user);
                          // To avoid trying to send more aTokens than available on balance, due to 1 wei imprecision
                          if (scaledDownLiquidationProtocolFee > scaledDownUserBalance) {
                            vars.liquidationProtocolFeeAmount = scaledDownUserBalance.rayMul(liquidityIndex);
                          }
                          vars.collateralAToken.transferOnLiquidation(
                            params.user,
                            vars.collateralAToken.RESERVE_TREASURY_ADDRESS(),
                            vars.liquidationProtocolFeeAmount
                          );
                        }
                        // Transfers the debt asset being repaid to the aToken, where the liquidity is kept
                        IERC20(params.debtAsset).safeTransferFrom(
                          msg.sender,
                          vars.debtReserveCache.aTokenAddress,
                          vars.actualDebtToLiquidate
                        );
                        IAToken(vars.debtReserveCache.aTokenAddress).handleRepayment(
                          msg.sender,
                          params.user,
                          vars.actualDebtToLiquidate
                        );
                        emit LiquidationCall(
                          params.collateralAsset,
                          params.debtAsset,
                          params.user,
                          vars.actualDebtToLiquidate,
                          vars.actualCollateralToLiquidate,
                          msg.sender,
                          params.receiveAToken
                        );
                      }
                      /**
                       * @notice Burns the collateral aTokens and transfers the underlying to the liquidator.
                       * @dev   The function also updates the state and the interest rate of the collateral reserve.
                       * @param collateralReserve The data of the collateral reserve
                       * @param params The additional parameters needed to execute the liquidation function
                       * @param vars The executeLiquidationCall() function local vars
                       */
                      function _burnCollateralATokens(
                        DataTypes.ReserveData storage collateralReserve,
                        DataTypes.ExecuteLiquidationCallParams memory params,
                        LiquidationCallLocalVars memory vars
                      ) internal {
                        DataTypes.ReserveCache memory collateralReserveCache = collateralReserve.cache();
                        collateralReserve.updateState(collateralReserveCache);
                        collateralReserve.updateInterestRatesAndVirtualBalance(
                          collateralReserveCache,
                          params.collateralAsset,
                          0,
                          vars.actualCollateralToLiquidate
                        );
                        // Burn the equivalent amount of aToken, sending the underlying to the liquidator
                        vars.collateralAToken.burn(
                          params.user,
                          msg.sender,
                          vars.actualCollateralToLiquidate,
                          collateralReserveCache.nextLiquidityIndex
                        );
                      }
                      /**
                       * @notice Liquidates the user aTokens by transferring them to the liquidator.
                       * @dev   The function also checks the state of the liquidator and activates the aToken as collateral
                       *        as in standard transfers if the isolation mode constraints are respected.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
                       * @param collateralReserve The data of the collateral reserve
                       * @param params The additional parameters needed to execute the liquidation function
                       * @param vars The executeLiquidationCall() function local vars
                       */
                      function _liquidateATokens(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
                        DataTypes.ReserveData storage collateralReserve,
                        DataTypes.ExecuteLiquidationCallParams memory params,
                        LiquidationCallLocalVars memory vars
                      ) internal {
                        uint256 liquidatorPreviousATokenBalance = IERC20(vars.collateralAToken).balanceOf(msg.sender);
                        vars.collateralAToken.transferOnLiquidation(
                          params.user,
                          msg.sender,
                          vars.actualCollateralToLiquidate
                        );
                        if (liquidatorPreviousATokenBalance == 0) {
                          DataTypes.UserConfigurationMap storage liquidatorConfig = usersConfig[msg.sender];
                          if (
                            ValidationLogic.validateAutomaticUseAsCollateral(
                              reservesData,
                              reservesList,
                              liquidatorConfig,
                              collateralReserve.configuration,
                              collateralReserve.aTokenAddress
                            )
                          ) {
                            liquidatorConfig.setUsingAsCollateral(collateralReserve.id, true);
                            emit ReserveUsedAsCollateralEnabled(params.collateralAsset, msg.sender);
                          }
                        }
                      }
                      /**
                       * @notice Burns the debt tokens of the user up to the amount being repaid by the liquidator.
                       * @dev The function alters the `debtReserveCache` state in `vars` to update the debt related data.
                       * @param params The additional parameters needed to execute the liquidation function
                       * @param vars the executeLiquidationCall() function local vars
                       */
                      function _burnDebtTokens(
                        DataTypes.ExecuteLiquidationCallParams memory params,
                        LiquidationCallLocalVars memory vars
                      ) internal {
                        vars.debtReserveCache.nextScaledVariableDebt = IVariableDebtToken(
                          vars.debtReserveCache.variableDebtTokenAddress
                        ).burn(params.user, vars.actualDebtToLiquidate, vars.debtReserveCache.nextVariableBorrowIndex);
                      }
                      /**
                       * @notice Calculates the total debt of the user and the actual amount to liquidate depending on the health factor
                       * and corresponding close factor.
                       * @dev If the Health Factor is below CLOSE_FACTOR_HF_THRESHOLD, the close factor is increased to MAX_LIQUIDATION_CLOSE_FACTOR
                       * @param debtReserveCache The reserve cache data object of the debt reserve
                       * @param params The additional parameters needed to execute the liquidation function
                       * @param healthFactor The health factor of the position
                       * @return The total debt of the user
                       * @return The actual debt to liquidate as a function of the closeFactor
                       */
                      function _calculateDebt(
                        DataTypes.ReserveCache memory debtReserveCache,
                        DataTypes.ExecuteLiquidationCallParams memory params,
                        uint256 healthFactor
                      ) internal view returns (uint256, uint256) {
                        uint256 userVariableDebt = IERC20(debtReserveCache.variableDebtTokenAddress).balanceOf(
                          params.user
                        );
                        uint256 closeFactor = healthFactor > CLOSE_FACTOR_HF_THRESHOLD
                          ? DEFAULT_LIQUIDATION_CLOSE_FACTOR
                          : MAX_LIQUIDATION_CLOSE_FACTOR;
                        uint256 maxLiquidatableDebt = userVariableDebt.percentMul(closeFactor);
                        uint256 actualDebtToLiquidate = params.debtToCover > maxLiquidatableDebt
                          ? maxLiquidatableDebt
                          : params.debtToCover;
                        return (userVariableDebt, actualDebtToLiquidate);
                      }
                      struct AvailableCollateralToLiquidateLocalVars {
                        uint256 collateralPrice;
                        uint256 debtAssetPrice;
                        uint256 maxCollateralToLiquidate;
                        uint256 baseCollateral;
                        uint256 bonusCollateral;
                        uint256 debtAssetDecimals;
                        uint256 collateralDecimals;
                        uint256 collateralAssetUnit;
                        uint256 debtAssetUnit;
                        uint256 collateralAmount;
                        uint256 debtAmountNeeded;
                        uint256 liquidationProtocolFeePercentage;
                        uint256 liquidationProtocolFee;
                      }
                      /**
                       * @notice Calculates how much of a specific collateral can be liquidated, given
                       * a certain amount of debt asset.
                       * @dev 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 debtReserveCache The cached 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
                       * @param liquidationBonus The collateral bonus percentage to receive as result of the liquidation
                       * @return The maximum amount that is possible to liquidate given all the liquidation constraints (user balance, close factor)
                       * @return The amount to repay with the liquidation
                       * @return The fee taken from the liquidation bonus amount to be paid to the protocol
                       */
                      function _calculateAvailableCollateralToLiquidate(
                        DataTypes.ReserveData storage collateralReserve,
                        DataTypes.ReserveCache memory debtReserveCache,
                        address collateralAsset,
                        address debtAsset,
                        uint256 debtToCover,
                        uint256 userCollateralBalance,
                        uint256 liquidationBonus,
                        IPriceOracleGetter oracle
                      ) internal view returns (uint256, uint256, uint256) {
                        AvailableCollateralToLiquidateLocalVars memory vars;
                        vars.collateralPrice = oracle.getAssetPrice(collateralAsset);
                        vars.debtAssetPrice = oracle.getAssetPrice(debtAsset);
                        vars.collateralDecimals = collateralReserve.configuration.getDecimals();
                        vars.debtAssetDecimals = debtReserveCache.reserveConfiguration.getDecimals();
                        unchecked {
                          vars.collateralAssetUnit = 10 ** vars.collateralDecimals;
                          vars.debtAssetUnit = 10 ** vars.debtAssetDecimals;
                        }
                        vars.liquidationProtocolFeePercentage = collateralReserve
                          .configuration
                          .getLiquidationProtocolFee();
                        // This is the base collateral to liquidate based on the given debt to cover
                        vars.baseCollateral =
                          ((vars.debtAssetPrice * debtToCover * vars.collateralAssetUnit)) /
                          (vars.collateralPrice * vars.debtAssetUnit);
                        vars.maxCollateralToLiquidate = vars.baseCollateral.percentMul(liquidationBonus);
                        if (vars.maxCollateralToLiquidate > userCollateralBalance) {
                          vars.collateralAmount = userCollateralBalance;
                          vars.debtAmountNeeded = ((vars.collateralPrice * vars.collateralAmount * vars.debtAssetUnit) /
                            (vars.debtAssetPrice * vars.collateralAssetUnit)).percentDiv(liquidationBonus);
                        } else {
                          vars.collateralAmount = vars.maxCollateralToLiquidate;
                          vars.debtAmountNeeded = debtToCover;
                        }
                        if (vars.liquidationProtocolFeePercentage != 0) {
                          vars.bonusCollateral =
                            vars.collateralAmount -
                            vars.collateralAmount.percentDiv(liquidationBonus);
                          vars.liquidationProtocolFee = vars.bonusCollateral.percentMul(
                            vars.liquidationProtocolFeePercentage
                          );
                          return (
                            vars.collateralAmount - vars.liquidationProtocolFee,
                            vars.debtAmountNeeded,
                            vars.liquidationProtocolFee
                          );
                        } else {
                          return (vars.collateralAmount, vars.debtAmountNeeded, 0);
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    library DataTypes {
                      /**
                       * This exists specifically to maintain the `getReserveData()` interface, since the new, internal
                       * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`.
                       */
                      struct ReserveDataLegacy {
                        //stores the reserve configuration
                        ReserveConfigurationMap configuration;
                        //the liquidity index. Expressed in ray
                        uint128 liquidityIndex;
                        //the current supply rate. Expressed in ray
                        uint128 currentLiquidityRate;
                        //variable borrow index. Expressed in ray
                        uint128 variableBorrowIndex;
                        //the current variable borrow rate. Expressed in ray
                        uint128 currentVariableBorrowRate;
                        // DEPRECATED on v3.2.0
                        uint128 currentStableBorrowRate;
                        //timestamp of last update
                        uint40 lastUpdateTimestamp;
                        //the id of the reserve. Represents the position in the list of the active reserves
                        uint16 id;
                        //aToken address
                        address aTokenAddress;
                        // DEPRECATED on v3.2.0
                        address stableDebtTokenAddress;
                        //variableDebtToken address
                        address variableDebtTokenAddress;
                        //address of the interest rate strategy
                        address interestRateStrategyAddress;
                        //the current treasury balance, scaled
                        uint128 accruedToTreasury;
                        //the outstanding unbacked aTokens minted through the bridging feature
                        uint128 unbacked;
                        //the outstanding debt borrowed against this asset in isolation mode
                        uint128 isolationModeTotalDebt;
                      }
                      struct ReserveData {
                        //stores the reserve configuration
                        ReserveConfigurationMap configuration;
                        //the liquidity index. Expressed in ray
                        uint128 liquidityIndex;
                        //the current supply rate. Expressed in ray
                        uint128 currentLiquidityRate;
                        //variable borrow index. Expressed in ray
                        uint128 variableBorrowIndex;
                        //the current variable borrow rate. Expressed in ray
                        uint128 currentVariableBorrowRate;
                        // DEPRECATED on v3.2.0
                        uint128 __deprecatedStableBorrowRate;
                        //timestamp of last update
                        uint40 lastUpdateTimestamp;
                        //the id of the reserve. Represents the position in the list of the active reserves
                        uint16 id;
                        //timestamp until when liquidations are not allowed on the reserve, if set to past liquidations will be allowed
                        uint40 liquidationGracePeriodUntil;
                        //aToken address
                        address aTokenAddress;
                        // DEPRECATED on v3.2.0
                        address __deprecatedStableDebtTokenAddress;
                        //variableDebtToken address
                        address variableDebtTokenAddress;
                        //address of the interest rate strategy
                        address interestRateStrategyAddress;
                        //the current treasury balance, scaled
                        uint128 accruedToTreasury;
                        //the outstanding unbacked aTokens minted through the bridging feature
                        uint128 unbacked;
                        //the outstanding debt borrowed against this asset in isolation mode
                        uint128 isolationModeTotalDebt;
                        //the amount of underlying accounted for by the protocol
                        uint128 virtualUnderlyingBalance;
                      }
                      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: DEPRECATED: stable rate borrowing enabled
                        //bit 60: asset is paused
                        //bit 61: borrowing in isolation mode is enabled
                        //bit 62: siloed borrowing enabled
                        //bit 63: flashloaning enabled
                        //bit 64-79: reserve factor
                        //bit 80-115: borrow cap in whole tokens, borrowCap == 0 => no cap
                        //bit 116-151: supply cap in whole tokens, supplyCap == 0 => no cap
                        //bit 152-167: liquidation protocol fee
                        //bit 168-175: DEPRECATED: eMode category
                        //bit 176-211: unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                        //bit 212-251: debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                        //bit 252: virtual accounting is enabled for the reserve
                        //bit 253-255 unused
                        uint256 data;
                      }
                      struct UserConfigurationMap {
                        /**
                         * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                         * The first bit indicates if an asset is used as collateral by the user, the second whether an
                         * asset is borrowed by the user.
                         */
                        uint256 data;
                      }
                      // DEPRECATED: kept for backwards compatibility, might be removed in a future version
                      struct EModeCategoryLegacy {
                        // each eMode category has a custom ltv and liquidation threshold
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        // DEPRECATED
                        address priceSource;
                        string label;
                      }
                      struct CollateralConfig {
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                      }
                      struct EModeCategoryBaseConfiguration {
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        string label;
                      }
                      struct EModeCategory {
                        // each eMode category has a custom ltv and liquidation threshold
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        uint128 collateralBitmap;
                        string label;
                        uint128 borrowableBitmap;
                      }
                      enum InterestRateMode {
                        NONE,
                        __DEPRECATED,
                        VARIABLE
                      }
                      struct ReserveCache {
                        uint256 currScaledVariableDebt;
                        uint256 nextScaledVariableDebt;
                        uint256 currLiquidityIndex;
                        uint256 nextLiquidityIndex;
                        uint256 currVariableBorrowIndex;
                        uint256 nextVariableBorrowIndex;
                        uint256 currLiquidityRate;
                        uint256 currVariableBorrowRate;
                        uint256 reserveFactor;
                        ReserveConfigurationMap reserveConfiguration;
                        address aTokenAddress;
                        address variableDebtTokenAddress;
                        uint40 reserveLastUpdateTimestamp;
                      }
                      struct ExecuteLiquidationCallParams {
                        uint256 reservesCount;
                        uint256 debtToCover;
                        address collateralAsset;
                        address debtAsset;
                        address user;
                        bool receiveAToken;
                        address priceOracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteSupplyParams {
                        address asset;
                        uint256 amount;
                        address onBehalfOf;
                        uint16 referralCode;
                      }
                      struct ExecuteBorrowParams {
                        address asset;
                        address user;
                        address onBehalfOf;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint16 referralCode;
                        bool releaseUnderlying;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteRepayParams {
                        address asset;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        address onBehalfOf;
                        bool useATokens;
                      }
                      struct ExecuteWithdrawParams {
                        address asset;
                        uint256 amount;
                        address to;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ExecuteSetUserEModeParams {
                        uint256 reservesCount;
                        address oracle;
                        uint8 categoryId;
                      }
                      struct FinalizeTransferParams {
                        address asset;
                        address from;
                        address to;
                        uint256 amount;
                        uint256 balanceFromBefore;
                        uint256 balanceToBefore;
                        uint256 reservesCount;
                        address oracle;
                        uint8 fromEModeCategory;
                      }
                      struct FlashloanParams {
                        address receiverAddress;
                        address[] assets;
                        uint256[] amounts;
                        uint256[] interestRateModes;
                        address onBehalfOf;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                        uint256 reservesCount;
                        address addressesProvider;
                        address pool;
                        uint8 userEModeCategory;
                        bool isAuthorizedFlashBorrower;
                      }
                      struct FlashloanSimpleParams {
                        address receiverAddress;
                        address asset;
                        uint256 amount;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                      }
                      struct FlashLoanRepaymentParams {
                        uint256 amount;
                        uint256 totalPremium;
                        uint256 flashLoanPremiumToProtocol;
                        address asset;
                        address receiverAddress;
                        uint16 referralCode;
                      }
                      struct CalculateUserAccountDataParams {
                        UserConfigurationMap userConfig;
                        uint256 reservesCount;
                        address user;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ValidateBorrowParams {
                        ReserveCache reserveCache;
                        UserConfigurationMap userConfig;
                        address asset;
                        address userAddress;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                        bool isolationModeActive;
                        address isolationModeCollateralAddress;
                        uint256 isolationModeDebtCeiling;
                      }
                      struct ValidateLiquidationCallParams {
                        ReserveCache debtReserveCache;
                        uint256 totalDebt;
                        uint256 healthFactor;
                        address priceOracleSentinel;
                      }
                      struct CalculateInterestRatesParams {
                        uint256 unbacked;
                        uint256 liquidityAdded;
                        uint256 liquidityTaken;
                        uint256 totalDebt;
                        uint256 reserveFactor;
                        address reserve;
                        bool usingVirtualBalance;
                        uint256 virtualUnderlyingBalance;
                      }
                      struct InitReserveParams {
                        address asset;
                        address aTokenAddress;
                        address variableDebtAddress;
                        address interestRateStrategyAddress;
                        uint16 reservesCount;
                        uint16 maxNumberReserves;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {Errors} from '../helpers/Errors.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    library BridgeLogic {
                      using ReserveLogic for DataTypes.ReserveCache;
                      using ReserveLogic for DataTypes.ReserveData;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using SafeCast for uint256;
                      using GPv2SafeERC20 for IERC20;
                      // See `IPool` for descriptions
                      event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                      event MintUnbacked(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                      /**
                       * @notice Mint unbacked aTokens to a user and updates the unbacked for the reserve.
                       * @dev Essentially a supply without transferring the underlying.
                       * @dev Emits the `MintUnbacked` event
                       * @dev Emits the `ReserveUsedAsCollateralEnabled` if asset is set as collateral
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param asset The address of the underlying asset to mint aTokens of
                       * @param amount The amount to mint
                       * @param onBehalfOf The address that will receive the aTokens
                       * @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 executeMintUnbacked(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        reserve.updateState(reserveCache);
                        ValidationLogic.validateSupply(reserveCache, reserve, amount, onBehalfOf);
                        uint256 unbackedMintCap = reserveCache.reserveConfiguration.getUnbackedMintCap();
                        uint256 reserveDecimals = reserveCache.reserveConfiguration.getDecimals();
                        uint256 unbacked = reserve.unbacked += amount.toUint128();
                        require(
                          unbacked <= unbackedMintCap * (10 ** reserveDecimals),
                          Errors.UNBACKED_MINT_CAP_EXCEEDED
                        );
                        reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, 0, 0);
                        bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
                          msg.sender,
                          onBehalfOf,
                          amount,
                          reserveCache.nextLiquidityIndex
                        );
                        if (isFirstSupply) {
                          if (
                            ValidationLogic.validateAutomaticUseAsCollateral(
                              reservesData,
                              reservesList,
                              userConfig,
                              reserveCache.reserveConfiguration,
                              reserveCache.aTokenAddress
                            )
                          ) {
                            userConfig.setUsingAsCollateral(reserve.id, true);
                            emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
                          }
                        }
                        emit MintUnbacked(asset, msg.sender, onBehalfOf, amount, referralCode);
                      }
                      /**
                       * @notice Back the current unbacked with `amount` and pay `fee`.
                       * @dev It is not possible to back more than the existing unbacked amount of the reserve
                       * @dev Emits the `BackUnbacked` event
                       * @param reserve The reserve to back unbacked for
                       * @param asset The address of the underlying asset to repay
                       * @param amount The amount to back
                       * @param fee The amount paid in fees
                       * @param protocolFeeBps The fraction of fees in basis points paid to the protocol
                       * @return The backed amount
                       */
                      function executeBackUnbacked(
                        DataTypes.ReserveData storage reserve,
                        address asset,
                        uint256 amount,
                        uint256 fee,
                        uint256 protocolFeeBps
                      ) external returns (uint256) {
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        reserve.updateState(reserveCache);
                        uint256 backingAmount = (amount < reserve.unbacked) ? amount : reserve.unbacked;
                        uint256 feeToProtocol = fee.percentMul(protocolFeeBps);
                        uint256 feeToLP = fee - feeToProtocol;
                        uint256 added = backingAmount + fee;
                        reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
                          IERC20(reserveCache.aTokenAddress).totalSupply() +
                            uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
                          feeToLP
                        );
                        reserve.accruedToTreasury += feeToProtocol.rayDiv(reserveCache.nextLiquidityIndex).toUint128();
                        reserve.unbacked -= backingAmount.toUint128();
                        reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, added, 0);
                        IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, added);
                        emit BackUnbacked(asset, msg.sender, backingAmount, fee);
                        return backingAmount;
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                    /**
                     * @title IERC20WithPermit
                     * @author Aave
                     * @notice Interface for the permit function (EIP-2612)
                     */
                    interface IERC20WithPermit is IERC20 {
                      /**
                       * @notice Allow passing a signed message to approve spending
                       * @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;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                    /**
                     * @title IPool
                     * @author Aave
                     * @notice Defines the basic interface for an Aave Pool.
                     */
                    interface IPool {
                      /**
                       * @dev Emitted on mintUnbacked()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param user The address initiating the supply
                       * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens
                       * @param amount The amount of supplied assets
                       * @param referralCode The referral code used
                       */
                      event MintUnbacked(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted on backUnbacked()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param backer The address paying for the backing
                       * @param amount The amount added as backing
                       * @param fee The amount paid in fees
                       */
                      event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                      /**
                       * @dev Emitted on supply()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param user The address initiating the supply
                       * @param onBehalfOf The beneficiary of the supply, receiving the aTokens
                       * @param amount The amount supplied
                       * @param referralCode The referral code used
                       */
                      event Supply(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted on withdraw()
                       * @param reserve The address of the underlying asset being withdrawn
                       * @param user The address initiating the withdrawal, owner of aTokens
                       * @param to The 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 interestRateMode The rate mode: 2 for Variable, 1 is deprecated (changed on v3.2.0)
                       * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray
                       * @param referralCode The referral code used
                       */
                      event Borrow(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 borrowRate,
                        uint16 indexed referralCode
                      );
                      /**
                       * @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
                       * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
                       */
                      event Repay(
                        address indexed reserve,
                        address indexed user,
                        address indexed repayer,
                        uint256 amount,
                        bool useATokens
                      );
                      /**
                       * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets
                       * @param asset The address of the underlying asset of the reserve
                       * @param totalDebt The total isolation mode debt for the reserve
                       */
                      event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                      /**
                       * @dev Emitted when the user selects a certain asset category for eMode
                       * @param user The address of the user
                       * @param categoryId The category id
                       */
                      event UserEModeSet(address indexed user, uint8 categoryId);
                      /**
                       * @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 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 interestRateMode The flashloan mode: 0 for regular flashloan,
                       *        1 for Stable (Deprecated on v3.2.0), 2 for Variable
                       * @param premium The fee flash borrowed
                       * @param referralCode The referral code used
                       */
                      event FlashLoan(
                        address indexed target,
                        address initiator,
                        address indexed asset,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 premium,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted when a borrower is liquidated.
                       * @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 liquidator
                       * @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.
                       * @param reserve The address of the underlying asset of the reserve
                       * @param liquidityRate The next liquidity rate
                       * @param stableBorrowRate The next stable borrow rate @note deprecated on v3.2.0
                       * @param variableBorrowRate The next variable borrow rate
                       * @param liquidityIndex The next liquidity index
                       * @param variableBorrowIndex The next variable borrow index
                       */
                      event ReserveDataUpdated(
                        address indexed reserve,
                        uint256 liquidityRate,
                        uint256 stableBorrowRate,
                        uint256 variableBorrowRate,
                        uint256 liquidityIndex,
                        uint256 variableBorrowIndex
                      );
                      /**
                       * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.
                       * @param reserve The address of the reserve
                       * @param amountMinted The amount minted to the treasury
                       */
                      event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                      /**
                       * @notice Mints an `amount` of aTokens to the `onBehalfOf`
                       * @param asset The address of the underlying asset to mint
                       * @param amount The amount to mint
                       * @param onBehalfOf The address that will receive the aTokens
                       * @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 mintUnbacked(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Back the current unbacked underlying with `amount` and pay `fee`.
                       * @param asset The address of the underlying asset to back
                       * @param amount The amount to back
                       * @param fee The amount paid in fees
                       * @return The backed amount
                       */
                      function backUnbacked(address asset, uint256 amount, uint256 fee) external returns (uint256);
                      /**
                       * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                       * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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 supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
                      /**
                       * @notice Supply with transfer approval of asset to be supplied done via permit function
                       * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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 deadline The deadline timestamp that the permit is valid
                       * @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 permitV The V parameter of ERC712 permit sig
                       * @param permitR The R parameter of ERC712 permit sig
                       * @param permitS The S parameter of ERC712 permit sig
                       */
                      function supplyWithPermit(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) external;
                      /**
                       * @notice 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 The 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);
                      /**
                       * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                       * already supplied enough collateral, or he was given enough allowance by a credit delegator on the VariableDebtToken
                       * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                       *   and 100 variable debt tokens
                       * @param asset The address of the underlying asset to borrow
                       * @param amount The amount to be borrowed
                       * @param interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                       * @param referralCode The 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 The 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 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 interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                       * @param onBehalfOf The 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 interestRateMode,
                        address onBehalfOf
                      ) external returns (uint256);
                      /**
                       * @notice Repay with transfer approval of asset to be repaid done via permit function
                       * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                       * @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 interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                       * @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
                       * @param deadline The deadline timestamp that the permit is valid
                       * @param permitV The V parameter of ERC712 permit sig
                       * @param permitR The R parameter of ERC712 permit sig
                       * @param permitS The S parameter of ERC712 permit sig
                       * @return The final amount repaid
                       */
                      function repayWithPermit(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode,
                        address onBehalfOf,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) external returns (uint256);
                      /**
                       * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
                       * equivalent debt tokens
                       * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable debt tokens
                       * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
                       * balance is not enough to cover the whole debt
                       * @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 interestRateMode DEPRECATED in v3.2.0
                       * @return The final amount repaid
                       */
                      function repayWithATokens(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode
                      ) external returns (uint256);
                      /**
                       * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
                       * @param asset The address of the underlying asset supplied
                       * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
                       */
                      function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                      /**
                       * @notice 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;
                      /**
                       * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                       * as long as the amount taken plus a fee is returned.
                       * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                       * into consideration. For further details please visit https://docs.aave.com/developers/
                       * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanReceiver interface
                       * @param assets The addresses of the assets being flash-borrowed
                       * @param amounts The amounts of the assets being flash-borrowed
                       * @param interestRateModes 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 -> Deprecated on v3.2.0
                       *   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 2 on `modes`
                       * @param params Variadic packed params to pass to the receiver as extra information
                       * @param referralCode The 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 interestRateModes,
                        address onBehalfOf,
                        bytes calldata params,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                       * as long as the amount taken plus a fee is returned.
                       * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                       * into consideration. For further details please visit https://docs.aave.com/developers/
                       * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface
                       * @param asset The address of the asset being flash-borrowed
                       * @param amount The amount of the asset being flash-borrowed
                       * @param params Variadic packed params to pass to the receiver as extra information
                       * @param referralCode The 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 flashLoanSimple(
                        address receiverAddress,
                        address asset,
                        uint256 amount,
                        bytes calldata params,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Returns the user account data across all the reserves
                       * @param user The address of the user
                       * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                       * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                       * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                       * @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 totalCollateralBase,
                          uint256 totalDebtBase,
                          uint256 availableBorrowsBase,
                          uint256 currentLiquidationThreshold,
                          uint256 ltv,
                          uint256 healthFactor
                        );
                      /**
                       * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                       * interest rate strategy
                       * @dev Only callable by the PoolConfigurator 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 variableDebtAddress 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 variableDebtAddress,
                        address interestRateStrategyAddress
                      ) external;
                      /**
                       * @notice Drop a reserve
                       * @dev Only callable by the PoolConfigurator contract
                       * @dev Does not reset eMode flags, which must be considered when reusing the same reserve id for a different reserve.
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function dropReserve(address asset) external;
                      /**
                       * @notice Updates the address of the interest rate strategy contract
                       * @dev Only callable by the PoolConfigurator 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;
                      /**
                       * @notice Accumulates interest to all indexes of the reserve
                       * @dev Only callable by the PoolConfigurator contract
                       * @dev To be used when required by the configurator, for example when updating interest rates strategy data
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function syncIndexesState(address asset) external;
                      /**
                       * @notice Updates interest rates on the reserve data
                       * @dev Only callable by the PoolConfigurator contract
                       * @dev To be used when required by the configurator, for example when updating interest rates strategy data
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function syncRatesState(address asset) external;
                      /**
                       * @notice Sets the configuration bitmap of the reserve as a whole
                       * @dev Only callable by the PoolConfigurator contract
                       * @param asset The address of the underlying asset of the reserve
                       * @param configuration The new configuration bitmap
                       */
                      function setConfiguration(
                        address asset,
                        DataTypes.ReserveConfigurationMap calldata configuration
                      ) external;
                      /**
                       * @notice 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);
                      /**
                       * @notice 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);
                      /**
                       * @notice Returns the 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);
                      /**
                       * @notice Returns the normalized variable debt per unit of asset
                       * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a
                       * "dynamic" variable index based on time, current stored index and virtual rate at the current
                       * moment (approx. a borrower would get if opening a position). This means that is always used in
                       * combination with variable debt supply/balances.
                       * If using this function externally, consider that is possible to have an increasing normalized
                       * variable debt that is not equivalent to how the variable debt index would be updated in storage
                       * (e.g. only updates with non-zero variable debt supply)
                       * @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);
                      /**
                       * @notice Returns the state and configuration of the reserve
                       * @param asset The address of the underlying asset of the reserve
                       * @return The state and configuration data of the reserve
                       */
                      function getReserveData(address asset) external view returns (DataTypes.ReserveDataLegacy memory);
                      /**
                       * @notice Returns the state and configuration of the reserve, including extra data included with Aave v3.1
                       * @dev DEPRECATED use independent getters instead (getReserveData, getLiquidationGracePeriod)
                       * @param asset The address of the underlying asset of the reserve
                       * @return The state and configuration data of the reserve with virtual accounting
                       */
                      function getReserveDataExtended(
                        address asset
                      ) external view returns (DataTypes.ReserveData memory);
                      /**
                       * @notice Returns the virtual underlying balance of the reserve
                       * @param asset The address of the underlying asset of the reserve
                       * @return The reserve virtual underlying balance
                       */
                      function getVirtualUnderlyingBalance(address asset) external view returns (uint128);
                      /**
                       * @notice Validates and finalizes an aToken transfer
                       * @dev 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;
                      /**
                       * @notice Returns the list of the underlying assets of all the initialized reserves
                       * @dev It does not include dropped reserves
                       * @return The addresses of the underlying assets of the initialized reserves
                       */
                      function getReservesList() external view returns (address[] memory);
                      /**
                       * @notice Returns the number of initialized reserves
                       * @dev It includes dropped reserves
                       * @return The count
                       */
                      function getReservesCount() external view returns (uint256);
                      /**
                       * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct
                       * @param id The id of the reserve as stored in the DataTypes.ReserveData struct
                       * @return The address of the reserve associated with id
                       */
                      function getReserveAddressById(uint16 id) external view returns (address);
                      /**
                       * @notice Returns the PoolAddressesProvider connected to this contract
                       * @return The address of the PoolAddressesProvider
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Updates the protocol fee on the bridging
                       * @param bridgeProtocolFee The part of the premium sent to the protocol treasury
                       */
                      function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;
                      /**
                       * @notice Updates flash loan premiums. Flash loan premium consists of two parts:
                       * - A part is sent to aToken holders as extra, one time accumulated interest
                       * - A part is collected by the protocol treasury
                       * @dev The total premium is calculated on the total borrowed amount
                       * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`
                       * @dev Only callable by the PoolConfigurator contract
                       * @param flashLoanPremiumTotal The total premium, expressed in bps
                       * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps
                       */
                      function updateFlashloanPremiums(
                        uint128 flashLoanPremiumTotal,
                        uint128 flashLoanPremiumToProtocol
                      ) external;
                      /**
                       * @notice Configures a new or alters an existing collateral configuration of an eMode.
                       * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.
                       * The category 0 is reserved as it's the default for volatile assets
                       * @param id The id of the category
                       * @param config The configuration of the category
                       */
                      function configureEModeCategory(
                        uint8 id,
                        DataTypes.EModeCategoryBaseConfiguration memory config
                      ) external;
                      /**
                       * @notice Replaces the current eMode collateralBitmap.
                       * @param id The id of the category
                       * @param collateralBitmap The collateralBitmap of the category
                       */
                      function configureEModeCategoryCollateralBitmap(uint8 id, uint128 collateralBitmap) external;
                      /**
                       * @notice Replaces the current eMode borrowableBitmap.
                       * @param id The id of the category
                       * @param borrowableBitmap The borrowableBitmap of the category
                       */
                      function configureEModeCategoryBorrowableBitmap(uint8 id, uint128 borrowableBitmap) external;
                      /**
                       * @notice Returns the data of an eMode category
                       * @dev DEPRECATED use independent getters instead
                       * @param id The id of the category
                       * @return The configuration data of the category
                       */
                      function getEModeCategoryData(
                        uint8 id
                      ) external view returns (DataTypes.EModeCategoryLegacy memory);
                      /**
                       * @notice Returns the label of an eMode category
                       * @param id The id of the category
                       * @return The label of the category
                       */
                      function getEModeCategoryLabel(uint8 id) external view returns (string memory);
                      /**
                       * @notice Returns the collateral config of an eMode category
                       * @param id The id of the category
                       * @return The ltv,lt,lb of the category
                       */
                      function getEModeCategoryCollateralConfig(
                        uint8 id
                      ) external view returns (DataTypes.CollateralConfig memory);
                      /**
                       * @notice Returns the collateralBitmap of an eMode category
                       * @param id The id of the category
                       * @return The collateralBitmap of the category
                       */
                      function getEModeCategoryCollateralBitmap(uint8 id) external view returns (uint128);
                      /**
                       * @notice Returns the borrowableBitmap of an eMode category
                       * @param id The id of the category
                       * @return The borrowableBitmap of the category
                       */
                      function getEModeCategoryBorrowableBitmap(uint8 id) external view returns (uint128);
                      /**
                       * @notice Allows a user to use the protocol in eMode
                       * @param categoryId The id of the category
                       */
                      function setUserEMode(uint8 categoryId) external;
                      /**
                       * @notice Returns the eMode the user is using
                       * @param user The address of the user
                       * @return The eMode id
                       */
                      function getUserEMode(address user) external view returns (uint256);
                      /**
                       * @notice Resets the isolation mode total debt of the given asset to zero
                       * @dev It requires the given asset has zero debt ceiling
                       * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                       */
                      function resetIsolationModeTotalDebt(address asset) external;
                      /**
                       * @notice Sets the liquidation grace period of the given asset
                       * @dev To enable a liquidation grace period, a timestamp in the future should be set,
                       *      To disable a liquidation grace period, any timestamp in the past works, like 0
                       * @param asset The address of the underlying asset to set the liquidationGracePeriod
                       * @param until Timestamp when the liquidation grace period will end
                       **/
                      function setLiquidationGracePeriod(address asset, uint40 until) external;
                      /**
                       * @notice Returns the liquidation grace period of the given asset
                       * @param asset The address of the underlying asset
                       * @return Timestamp when the liquidation grace period will end
                       **/
                      function getLiquidationGracePeriod(address asset) external returns (uint40);
                      /**
                       * @notice Returns the total fee on flash loans
                       * @return The total fee on flashloans
                       */
                      function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);
                      /**
                       * @notice Returns the part of the bridge fees sent to protocol
                       * @return The bridge fee sent to the protocol treasury
                       */
                      function BRIDGE_PROTOCOL_FEE() external view returns (uint256);
                      /**
                       * @notice Returns the part of the flashloan fees sent to protocol
                       * @return The flashloan fee sent to the protocol treasury
                       */
                      function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);
                      /**
                       * @notice Returns the maximum number of reserves supported to be listed in this Pool
                       * @return The maximum number of reserves supported
                       */
                      function MAX_NUMBER_RESERVES() external view returns (uint16);
                      /**
                       * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                       * @param assets The list of reserves for which the minting needs to be executed
                       */
                      function mintToTreasury(address[] calldata assets) external;
                      /**
                       * @notice Rescue and transfer tokens locked in this contract
                       * @param token The address of the token
                       * @param to The address of the recipient
                       * @param amount The amount of token to transfer
                       */
                      function rescueTokens(address token, address to, uint256 amount) external;
                      /**
                       * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                       * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                       * @dev Deprecated: Use the `supply` function instead
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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;
                      /**
                       * @notice Gets the address of the external FlashLoanLogic
                       */
                      function getFlashLoanLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external BorrowLogic
                       */
                      function getBorrowLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external BridgeLogic
                       */
                      function getBridgeLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external EModeLogic
                       */
                      function getEModeLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external LiquidationLogic
                       */
                      function getLiquidationLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external PoolLogic
                       */
                      function getPoolLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external SupplyLogic
                       */
                      function getSupplyLogic() external view returns (address);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    /**
                     * @title IACLManager
                     * @author Aave
                     * @notice Defines the basic interface for the ACL Manager
                     */
                    interface IACLManager {
                      /**
                       * @notice Returns the contract address of the PoolAddressesProvider
                       * @return The address of the PoolAddressesProvider
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Returns the identifier of the PoolAdmin role
                       * @return The id of the PoolAdmin role
                       */
                      function POOL_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the EmergencyAdmin role
                       * @return The id of the EmergencyAdmin role
                       */
                      function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the RiskAdmin role
                       * @return The id of the RiskAdmin role
                       */
                      function RISK_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the FlashBorrower role
                       * @return The id of the FlashBorrower role
                       */
                      function FLASH_BORROWER_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the Bridge role
                       * @return The id of the Bridge role
                       */
                      function BRIDGE_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the AssetListingAdmin role
                       * @return The id of the AssetListingAdmin role
                       */
                      function ASSET_LISTING_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Set the role as admin of a specific role.
                       * @dev By default the admin role for all roles is `DEFAULT_ADMIN_ROLE`.
                       * @param role The role to be managed by the admin role
                       * @param adminRole The admin role
                       */
                      function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
                      /**
                       * @notice Adds a new admin as PoolAdmin
                       * @param admin The address of the new admin
                       */
                      function addPoolAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as PoolAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removePoolAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is PoolAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is PoolAdmin, false otherwise
                       */
                      function isPoolAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new admin as EmergencyAdmin
                       * @param admin The address of the new admin
                       */
                      function addEmergencyAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as EmergencyAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeEmergencyAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is EmergencyAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is EmergencyAdmin, false otherwise
                       */
                      function isEmergencyAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new admin as RiskAdmin
                       * @param admin The address of the new admin
                       */
                      function addRiskAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as RiskAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeRiskAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is RiskAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is RiskAdmin, false otherwise
                       */
                      function isRiskAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new address as FlashBorrower
                       * @param borrower The address of the new FlashBorrower
                       */
                      function addFlashBorrower(address borrower) external;
                      /**
                       * @notice Removes an address as FlashBorrower
                       * @param borrower The address of the FlashBorrower to remove
                       */
                      function removeFlashBorrower(address borrower) external;
                      /**
                       * @notice Returns true if the address is FlashBorrower, false otherwise
                       * @param borrower The address to check
                       * @return True if the given address is FlashBorrower, false otherwise
                       */
                      function isFlashBorrower(address borrower) external view returns (bool);
                      /**
                       * @notice Adds a new address as Bridge
                       * @param bridge The address of the new Bridge
                       */
                      function addBridge(address bridge) external;
                      /**
                       * @notice Removes an address as Bridge
                       * @param bridge The address of the bridge to remove
                       */
                      function removeBridge(address bridge) external;
                      /**
                       * @notice Returns true if the address is Bridge, false otherwise
                       * @param bridge The address to check
                       * @return True if the given address is Bridge, false otherwise
                       */
                      function isBridge(address bridge) external view returns (bool);
                      /**
                       * @notice Adds a new admin as AssetListingAdmin
                       * @param admin The address of the new admin
                       */
                      function addAssetListingAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as AssetListingAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeAssetListingAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is AssetListingAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is AssetListingAdmin, false otherwise
                       */
                      function isAssetListingAdmin(address admin) external view returns (bool);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
                    import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
                    import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
                    import {DataTypes} from '../libraries/types/DataTypes.sol';
                    /**
                     * @title PoolStorage
                     * @author Aave
                     * @notice Contract used as storage of the Pool contract.
                     * @dev It defines the storage layout of the Pool contract.
                     */
                    contract PoolStorage {
                      using ReserveLogic for DataTypes.ReserveData;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      // Map of reserves and their data (underlyingAssetOfReserve => reserveData)
                      mapping(address => DataTypes.ReserveData) internal _reserves;
                      // Map of users address and their configuration data (userAddress => userConfiguration)
                      mapping(address => DataTypes.UserConfigurationMap) internal _usersConfig;
                      // List of reserves as a map (reserveId => reserve).
                      // It is structured as a mapping for gas savings reasons, using the reserve id as index
                      mapping(uint256 => address) internal _reservesList;
                      // List of eMode categories as a map (eModeCategoryId => eModeCategory).
                      // It is structured as a mapping for gas savings reasons, using the eModeCategoryId as index
                      mapping(uint8 => DataTypes.EModeCategory) internal _eModeCategories;
                      // Map of users address and their eMode category (userAddress => eModeCategoryId)
                      mapping(address => uint8) internal _usersEModeCategory;
                      // Fee of the protocol bridge, expressed in bps
                      uint256 internal _bridgeProtocolFee;
                      // Total FlashLoan Premium, expressed in bps
                      uint128 internal _flashLoanPremiumTotal;
                      // FlashLoan premium paid to protocol treasury, expressed in bps
                      uint128 internal _flashLoanPremiumToProtocol;
                      // DEPRECATED on v3.2.0
                      uint64 internal __DEPRECATED_maxStableRateBorrowSizePercent;
                      // Maximum number of active reserves there have been in the protocol. It is the upper bound of the reserves list
                      uint16 internal _reservesCount;
                    }
                    // SPDX-License-Identifier: LGPL-3.0-or-later
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../openzeppelin/contracts/IERC20.sol';
                    /// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
                    /// @author Gnosis Developers
                    /// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract.
                    library GPv2SafeERC20 {
                      /// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
                      /// also when the token returns `false`.
                      function safeTransfer(IERC20 token, address to, uint256 value) internal {
                        bytes4 selector_ = token.transfer.selector;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          let freeMemoryPointer := mload(0x40)
                          mstore(freeMemoryPointer, selector_)
                          mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 36), value)
                          if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
                            returndatacopy(0, 0, returndatasize())
                            revert(0, returndatasize())
                          }
                        }
                        require(getLastTransferResult(token), 'GPv2: failed transfer');
                      }
                      /// @dev Wrapper around a call to the ERC20 function `transferFrom` that
                      /// reverts also when the token returns `false`.
                      function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                        bytes4 selector_ = token.transferFrom.selector;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          let freeMemoryPointer := mload(0x40)
                          mstore(freeMemoryPointer, selector_)
                          mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 68), value)
                          if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
                            returndatacopy(0, 0, returndatasize())
                            revert(0, returndatasize())
                          }
                        }
                        require(getLastTransferResult(token), 'GPv2: failed transferFrom');
                      }
                      /// @dev Verifies that the last return was a successful `transfer*` call.
                      /// This is done by checking that the return data is either empty, or
                      /// is a valid ABI encoded boolean.
                      function getLastTransferResult(IERC20 token) private view returns (bool success) {
                        // NOTE: Inspecting previous return data requires assembly. Note that
                        // we write the return data to memory 0 in the case where the return
                        // data size is 32, this is OK since the first 64 bytes of memory are
                        // reserved by Solidy as a scratch space that can be used within
                        // assembly blocks.
                        // <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          /// @dev Revert with an ABI encoded Solidity error with a message
                          /// that fits into 32-bytes.
                          ///
                          /// An ABI encoded Solidity error has the following memory layout:
                          ///
                          /// ------------+----------------------------------
                          ///  byte range | value
                          /// ------------+----------------------------------
                          ///  0x00..0x04 |        selector("Error(string)")
                          ///  0x04..0x24 |      string offset (always 0x20)
                          ///  0x24..0x44 |                    string length
                          ///  0x44..0x64 | string value, padded to 32-bytes
                          function revertWithMessage(length, message) {
                            mstore(0x00, '\\x08\\xc3\\x79\\xa0')
                            mstore(0x04, 0x20)
                            mstore(0x24, length)
                            mstore(0x44, message)
                            revert(0x00, 0x64)
                          }
                          switch returndatasize()
                          // Non-standard ERC20 transfer without return.
                          case 0 {
                            // NOTE: When the return data size is 0, verify that there
                            // is code at the address. This is done in order to maintain
                            // compatibility with Solidity calling conventions.
                            // <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
                            if iszero(extcodesize(token)) {
                              revertWithMessage(20, 'GPv2: not a contract')
                            }
                            success := 1
                          }
                          // Standard ERC20 transfer returning boolean success value.
                          case 32 {
                            returndatacopy(0, 0, returndatasize())
                            // NOTE: For ABI encoding v1, any non-zero value is accepted
                            // as `true` for a boolean. In order to stay compatible with
                            // OpenZeppelin's `SafeERC20` library which is known to work
                            // with the existing ERC20 implementation we care about,
                            // make sure we return success for any non-zero return value
                            // from the `transfer*` call.
                            success := iszero(iszero(mload(0)))
                          }
                          default {
                            revertWithMessage(31, 'GPv2: malformed transfer result')
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @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) {
                        // This method relies on extcodesize, which returns 0 for contracts in
                        // construction, since the code is only stored at the end of the
                        // constructor execution.
                        uint256 size;
                        assembly {
                          size := extcodesize(account)
                        }
                        return size > 0;
                      }
                      /**
                       * @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');
                        (bool success, ) = recipient.call{value: amount}('');
                        require(success, 'Address: unable to send value, recipient may have reverted');
                      }
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCall(target, data, 'Address: low-level call failed');
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                       * `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                      ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       *
                       * _Available since v3.1._
                       */
                      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');
                      }
                      /**
                       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                       * with `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      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');
                        require(isContract(target), 'Address: call to non-contract');
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(
                        address target,
                        bytes memory data
                      ) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, 'Address: low-level static call failed');
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                      ) internal view returns (bytes memory) {
                        require(isContract(target), 'Address: static call to non-contract');
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, 'Address: low-level delegate call failed');
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                      ) internal returns (bytes memory) {
                        require(isContract(target), 'Address: delegate call to non-contract');
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason using the provided one.
                       *
                       * _Available since v4.3._
                       */
                      function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                      ) internal pure returns (bytes memory) {
                        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
                            assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                            }
                          } else {
                            revert(errorMessage);
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @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.8.0;
                    import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                    import {IInitializableAToken} from './IInitializableAToken.sol';
                    /**
                     * @title IAToken
                     * @author Aave
                     * @notice Defines the basic interface for an AToken.
                     */
                    interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
                      /**
                       * @dev Emitted during the transfer action
                       * @param from The user whose tokens are being transferred
                       * @param to The recipient
                       * @param value The scaled amount being transferred
                       * @param index The next liquidity index of the reserve
                       */
                      event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index);
                      /**
                       * @notice Mints `amount` aTokens to `user`
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the minted aTokens
                       * @param amount The amount of tokens getting minted
                       * @param index The next liquidity index of the reserve
                       * @return `true` if the the previous balance of the user was 0
                       */
                      function mint(
                        address caller,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) external returns (bool);
                      /**
                       * @notice Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                       * @dev In some instances, the mint event could be emitted from a burn transaction
                       * if the amount to burn is less than the interest that the user accrued
                       * @param from The address from which the aTokens will be burned
                       * @param receiverOfUnderlying The address that will receive the underlying
                       * @param amount The amount being burned
                       * @param index The next liquidity index of the reserve
                       */
                      function burn(address from, address receiverOfUnderlying, uint256 amount, uint256 index) external;
                      /**
                       * @notice Mints aTokens to the reserve treasury
                       * @param amount The amount of tokens getting minted
                       * @param index The next liquidity index of the reserve
                       */
                      function mintToTreasury(uint256 amount, uint256 index) external;
                      /**
                       * @notice 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;
                      /**
                       * @notice Transfers the underlying asset to `target`.
                       * @dev Used by the Pool to transfer assets in borrow(), withdraw() and flashLoan()
                       * @param target The recipient of the underlying
                       * @param amount The amount getting transferred
                       */
                      function transferUnderlyingTo(address target, uint256 amount) external;
                      /**
                       * @notice Handles the underlying received by the aToken after the transfer has been completed.
                       * @dev The default implementation is empty as with standard ERC20 tokens, nothing needs to be done after the
                       * transfer is concluded. However in the future there may be aTokens that allow for example to stake the underlying
                       * to receive LM rewards. In that case, `handleRepayment()` would perform the staking of the underlying asset.
                       * @param user The user executing the repayment
                       * @param onBehalfOf The address of the user who will get his debt reduced/removed
                       * @param amount The amount getting repaid
                       */
                      function handleRepayment(address user, address onBehalfOf, uint256 amount) external;
                      /**
                       * @notice Allow passing a signed message to approve spending
                       * @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;
                      /**
                       * @notice Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @return The address of the underlying asset
                       */
                      function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                      /**
                       * @notice Returns the address of the Aave treasury, receiving the fees on this aToken.
                       * @return Address of the Aave treasury
                       */
                      function RESERVE_TREASURY_ADDRESS() external view returns (address);
                      /**
                       * @notice Get the domain separator for the token
                       * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                       * @return The domain separator of the token at current chain
                       */
                      function DOMAIN_SEPARATOR() external view returns (bytes32);
                      /**
                       * @notice Returns the nonce for owner.
                       * @param owner The address of the owner
                       * @return The nonce of the owner
                       */
                      function nonces(address owner) external view returns (uint256);
                      /**
                       * @notice Rescue and transfer tokens locked in this contract
                       * @param token The address of the token
                       * @param to The address of the recipient
                       * @param amount The amount of token to transfer
                       */
                      function rescueTokens(address token, address to, uint256 amount) external;
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @title WadRayMath library
                     * @author Aave
                     * @notice Provides functions to perform calculations with Wad and Ray units
                     * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                     * with 27 digits of precision)
                     * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                     */
                    library WadRayMath {
                      // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                      uint256 internal constant WAD = 1e18;
                      uint256 internal constant HALF_WAD = 0.5e18;
                      uint256 internal constant RAY = 1e27;
                      uint256 internal constant HALF_RAY = 0.5e27;
                      uint256 internal constant WAD_RAY_RATIO = 1e9;
                      /**
                       * @dev Multiplies two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a*b, in wad
                       */
                      function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_WAD), WAD)
                        }
                      }
                      /**
                       * @dev Divides two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a/b, in wad
                       */
                      function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, WAD), div(b, 2)), b)
                        }
                      }
                      /**
                       * @notice Multiplies two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raymul b
                       */
                      function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_RAY), RAY)
                        }
                      }
                      /**
                       * @notice Divides two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raydiv b
                       */
                      function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, RAY), div(b, 2)), b)
                        }
                      }
                      /**
                       * @dev Casts ray down to wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @return b = a converted to wad, rounded half up to the nearest wad
                       */
                      function rayToWad(uint256 a) internal pure returns (uint256 b) {
                        assembly {
                          b := div(a, WAD_RAY_RATIO)
                          let remainder := mod(a, WAD_RAY_RATIO)
                          if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                            b := add(b, 1)
                          }
                        }
                      }
                      /**
                       * @dev Converts wad up to ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @return b = a converted in ray
                       */
                      function wadToRay(uint256 a) internal pure returns (uint256 b) {
                        // to avoid overflow, b/WAD_RAY_RATIO == a
                        assembly {
                          b := mul(a, WAD_RAY_RATIO)
                          if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                            revert(0, 0)
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {Address} from '../../../dependencies/openzeppelin/contracts/Address.sol';
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
                    import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                    import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {IPriceOracleSentinel} from '../../../interfaces/IPriceOracleSentinel.sol';
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {IAccessControl} from '../../../dependencies/openzeppelin/contracts/IAccessControl.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
                    import {Errors} from '../helpers/Errors.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {GenericLogic} from './GenericLogic.sol';
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {IncentivizedERC20} from '../../tokenization/base/IncentivizedERC20.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 WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using SafeCast for uint256;
                      using GPv2SafeERC20 for IERC20;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using Address for address;
                      // Factor to apply to "only-variable-debt" liquidity rate to get threshold for rebalancing, expressed in bps
                      // A value of 0.9e4 results in 90%
                      uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 0.9e4;
                      // Minimum health factor allowed under any circumstance
                      // A value of 0.95e18 results in 0.95
                      uint256 public constant MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 0.95e18;
                      /**
                       * @dev Minimum health factor to consider a user position healthy
                       * A value of 1e18 results in 1
                       */
                      uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;
                      /**
                       * @dev Role identifier for the role allowed to supply isolated reserves as collateral
                       */
                      bytes32 public constant ISOLATED_COLLATERAL_SUPPLIER_ROLE =
                        keccak256('ISOLATED_COLLATERAL_SUPPLIER');
                      /**
                       * @notice Validates a supply action.
                       * @param reserveCache The cached data of the reserve
                       * @param amount The amount to be supplied
                       */
                      function validateSupply(
                        DataTypes.ReserveCache memory reserveCache,
                        DataTypes.ReserveData storage reserve,
                        uint256 amount,
                        address onBehalfOf
                      ) internal view {
                        require(amount != 0, Errors.INVALID_AMOUNT);
                        (bool isActive, bool isFrozen, , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                        require(isActive, Errors.RESERVE_INACTIVE);
                        require(!isPaused, Errors.RESERVE_PAUSED);
                        require(!isFrozen, Errors.RESERVE_FROZEN);
                        require(onBehalfOf != reserveCache.aTokenAddress, Errors.SUPPLY_TO_ATOKEN);
                        uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCap();
                        require(
                          supplyCap == 0 ||
                            ((IAToken(reserveCache.aTokenAddress).scaledTotalSupply() +
                              uint256(reserve.accruedToTreasury)).rayMul(reserveCache.nextLiquidityIndex) + amount) <=
                            supplyCap * (10 ** reserveCache.reserveConfiguration.getDecimals()),
                          Errors.SUPPLY_CAP_EXCEEDED
                        );
                      }
                      /**
                       * @notice Validates a withdraw action.
                       * @param reserveCache The cached data of the reserve
                       * @param amount The amount to be withdrawn
                       * @param userBalance The balance of the user
                       */
                      function validateWithdraw(
                        DataTypes.ReserveCache memory reserveCache,
                        uint256 amount,
                        uint256 userBalance
                      ) internal pure {
                        require(amount != 0, Errors.INVALID_AMOUNT);
                        require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);
                        (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                        require(isActive, Errors.RESERVE_INACTIVE);
                        require(!isPaused, Errors.RESERVE_PAUSED);
                      }
                      struct ValidateBorrowLocalVars {
                        uint256 currentLtv;
                        uint256 collateralNeededInBaseCurrency;
                        uint256 userCollateralInBaseCurrency;
                        uint256 userDebtInBaseCurrency;
                        uint256 availableLiquidity;
                        uint256 healthFactor;
                        uint256 totalDebt;
                        uint256 totalSupplyVariableDebt;
                        uint256 reserveDecimals;
                        uint256 borrowCap;
                        uint256 amountInBaseCurrency;
                        uint256 assetUnit;
                        address siloedBorrowingAddress;
                        bool isActive;
                        bool isFrozen;
                        bool isPaused;
                        bool borrowingEnabled;
                        bool siloedBorrowingEnabled;
                      }
                      /**
                       * @notice Validates a borrow action.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param params Additional params needed for the validation
                       */
                      function validateBorrow(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.ValidateBorrowParams memory params
                      ) internal view {
                        require(params.amount != 0, Errors.INVALID_AMOUNT);
                        ValidateBorrowLocalVars memory vars;
                        (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.isPaused) = params
                          .reserveCache
                          .reserveConfiguration
                          .getFlags();
                        require(vars.isActive, Errors.RESERVE_INACTIVE);
                        require(!vars.isPaused, Errors.RESERVE_PAUSED);
                        require(!vars.isFrozen, Errors.RESERVE_FROZEN);
                        require(vars.borrowingEnabled, Errors.BORROWING_NOT_ENABLED);
                        require(
                          !params.reserveCache.reserveConfiguration.getIsVirtualAccActive() ||
                            IERC20(params.reserveCache.aTokenAddress).totalSupply() >= params.amount,
                          Errors.INVALID_AMOUNT
                        );
                        require(
                          params.priceOracleSentinel == address(0) ||
                            IPriceOracleSentinel(params.priceOracleSentinel).isBorrowAllowed(),
                          Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                        );
                        //validate interest rate mode
                        require(
                          params.interestRateMode == DataTypes.InterestRateMode.VARIABLE,
                          Errors.INVALID_INTEREST_RATE_MODE_SELECTED
                        );
                        vars.reserveDecimals = params.reserveCache.reserveConfiguration.getDecimals();
                        vars.borrowCap = params.reserveCache.reserveConfiguration.getBorrowCap();
                        unchecked {
                          vars.assetUnit = 10 ** vars.reserveDecimals;
                        }
                        if (vars.borrowCap != 0) {
                          vars.totalSupplyVariableDebt = params.reserveCache.currScaledVariableDebt.rayMul(
                            params.reserveCache.nextVariableBorrowIndex
                          );
                          vars.totalDebt = vars.totalSupplyVariableDebt + params.amount;
                          unchecked {
                            require(vars.totalDebt <= vars.borrowCap * vars.assetUnit, Errors.BORROW_CAP_EXCEEDED);
                          }
                        }
                        if (params.isolationModeActive) {
                          // check that the asset being borrowed is borrowable in isolation mode AND
                          // the total exposure is no bigger than the collateral debt ceiling
                          require(
                            params.reserveCache.reserveConfiguration.getBorrowableInIsolation(),
                            Errors.ASSET_NOT_BORROWABLE_IN_ISOLATION
                          );
                          require(
                            reservesData[params.isolationModeCollateralAddress].isolationModeTotalDebt +
                              (params.amount /
                                10 ** (vars.reserveDecimals - ReserveConfiguration.DEBT_CEILING_DECIMALS))
                                .toUint128() <=
                              params.isolationModeDebtCeiling,
                            Errors.DEBT_CEILING_EXCEEDED
                          );
                        }
                        if (params.userEModeCategory != 0) {
                          require(
                            EModeConfiguration.isReserveEnabledOnBitmap(
                              eModeCategories[params.userEModeCategory].borrowableBitmap,
                              reservesData[params.asset].id
                            ),
                            Errors.NOT_BORROWABLE_IN_EMODE
                          );
                        }
                        (
                          vars.userCollateralInBaseCurrency,
                          vars.userDebtInBaseCurrency,
                          vars.currentLtv,
                          ,
                          vars.healthFactor,
                        ) = GenericLogic.calculateUserAccountData(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          DataTypes.CalculateUserAccountDataParams({
                            userConfig: params.userConfig,
                            reservesCount: params.reservesCount,
                            user: params.userAddress,
                            oracle: params.oracle,
                            userEModeCategory: params.userEModeCategory
                          })
                        );
                        require(vars.userCollateralInBaseCurrency != 0, Errors.COLLATERAL_BALANCE_IS_ZERO);
                        require(vars.currentLtv != 0, Errors.LTV_VALIDATION_FAILED);
                        require(
                          vars.healthFactor > HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                          Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                        );
                        vars.amountInBaseCurrency =
                          IPriceOracleGetter(params.oracle).getAssetPrice(params.asset) *
                          params.amount;
                        unchecked {
                          vars.amountInBaseCurrency /= vars.assetUnit;
                        }
                        //add the current already borrowed amount to the amount requested to calculate the total collateral needed.
                        vars.collateralNeededInBaseCurrency = (vars.userDebtInBaseCurrency + vars.amountInBaseCurrency)
                          .percentDiv(vars.currentLtv); //LTV is calculated in percentage
                        require(
                          vars.collateralNeededInBaseCurrency <= vars.userCollateralInBaseCurrency,
                          Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW
                        );
                        if (params.userConfig.isBorrowingAny()) {
                          (vars.siloedBorrowingEnabled, vars.siloedBorrowingAddress) = params
                            .userConfig
                            .getSiloedBorrowingState(reservesData, reservesList);
                          if (vars.siloedBorrowingEnabled) {
                            require(vars.siloedBorrowingAddress == params.asset, Errors.SILOED_BORROWING_VIOLATION);
                          } else {
                            require(
                              !params.reserveCache.reserveConfiguration.getSiloedBorrowing(),
                              Errors.SILOED_BORROWING_VIOLATION
                            );
                          }
                        }
                      }
                      /**
                       * @notice Validates a repay action.
                       * @param reserveCache The cached data of the reserve
                       * @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 debt The borrow balance of the user
                       */
                      function validateRepay(
                        DataTypes.ReserveCache memory reserveCache,
                        uint256 amountSent,
                        DataTypes.InterestRateMode interestRateMode,
                        address onBehalfOf,
                        uint256 debt
                      ) internal view {
                        require(amountSent != 0, Errors.INVALID_AMOUNT);
                        require(
                          interestRateMode == DataTypes.InterestRateMode.VARIABLE,
                          Errors.INVALID_INTEREST_RATE_MODE_SELECTED
                        );
                        require(
                          amountSent != type(uint256).max || msg.sender == onBehalfOf,
                          Errors.NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF
                        );
                        (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                        require(isActive, Errors.RESERVE_INACTIVE);
                        require(!isPaused, Errors.RESERVE_PAUSED);
                        require(debt != 0, Errors.NO_DEBT_OF_SELECTED_TYPE);
                      }
                      /**
                       * @notice Validates the action of setting an asset as collateral.
                       * @param reserveCache The cached data of the reserve
                       * @param userBalance The balance of the user
                       */
                      function validateSetUseReserveAsCollateral(
                        DataTypes.ReserveCache memory reserveCache,
                        uint256 userBalance
                      ) internal pure {
                        require(userBalance != 0, Errors.UNDERLYING_BALANCE_ZERO);
                        (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                        require(isActive, Errors.RESERVE_INACTIVE);
                        require(!isPaused, Errors.RESERVE_PAUSED);
                      }
                      /**
                       * @notice Validates a flashloan action.
                       * @param reservesData The state of all the reserves
                       * @param assets The assets being flash-borrowed
                       * @param amounts The amounts for each asset being borrowed
                       */
                      function validateFlashloan(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        address[] memory assets,
                        uint256[] memory amounts
                      ) internal view {
                        require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS);
                        for (uint256 i = 0; i < assets.length; i++) {
                          for (uint256 j = i + 1; j < assets.length; j++) {
                            require(assets[i] != assets[j], Errors.INCONSISTENT_FLASHLOAN_PARAMS);
                          }
                          validateFlashloanSimple(reservesData[assets[i]], amounts[i]);
                        }
                      }
                      /**
                       * @notice Validates a flashloan action.
                       * @param reserve The state of the reserve
                       */
                      function validateFlashloanSimple(
                        DataTypes.ReserveData storage reserve,
                        uint256 amount
                      ) internal view {
                        DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration;
                        require(!configuration.getPaused(), Errors.RESERVE_PAUSED);
                        require(configuration.getActive(), Errors.RESERVE_INACTIVE);
                        require(configuration.getFlashLoanEnabled(), Errors.FLASHLOAN_DISABLED);
                        require(
                          !configuration.getIsVirtualAccActive() ||
                            IERC20(reserve.aTokenAddress).totalSupply() >= amount,
                          Errors.INVALID_AMOUNT
                        );
                      }
                      struct ValidateLiquidationCallLocalVars {
                        bool collateralReserveActive;
                        bool collateralReservePaused;
                        bool principalReserveActive;
                        bool principalReservePaused;
                        bool isCollateralEnabled;
                      }
                      /**
                       * @notice Validates the liquidation action.
                       * @param userConfig The user configuration mapping
                       * @param collateralReserve The reserve data of the collateral
                       * @param debtReserve The reserve data of the debt
                       * @param params Additional parameters needed for the validation
                       */
                      function validateLiquidationCall(
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ReserveData storage collateralReserve,
                        DataTypes.ReserveData storage debtReserve,
                        DataTypes.ValidateLiquidationCallParams memory params
                      ) internal view {
                        ValidateLiquidationCallLocalVars memory vars;
                        (vars.collateralReserveActive, , , vars.collateralReservePaused) = collateralReserve
                          .configuration
                          .getFlags();
                        (vars.principalReserveActive, , , vars.principalReservePaused) = params
                          .debtReserveCache
                          .reserveConfiguration
                          .getFlags();
                        require(vars.collateralReserveActive && vars.principalReserveActive, Errors.RESERVE_INACTIVE);
                        require(!vars.collateralReservePaused && !vars.principalReservePaused, Errors.RESERVE_PAUSED);
                        require(
                          params.priceOracleSentinel == address(0) ||
                            params.healthFactor < MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD ||
                            IPriceOracleSentinel(params.priceOracleSentinel).isLiquidationAllowed(),
                          Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                        );
                        require(
                          collateralReserve.liquidationGracePeriodUntil < uint40(block.timestamp) &&
                            debtReserve.liquidationGracePeriodUntil < uint40(block.timestamp),
                          Errors.LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED
                        );
                        require(
                          params.healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                          Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
                        );
                        vars.isCollateralEnabled =
                          collateralReserve.configuration.getLiquidationThreshold() != 0 &&
                          userConfig.isUsingAsCollateral(collateralReserve.id);
                        //if collateral isn't enabled as collateral by user, it cannot be liquidated
                        require(vars.isCollateralEnabled, Errors.COLLATERAL_CANNOT_BE_LIQUIDATED);
                        require(params.totalDebt != 0, Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER);
                      }
                      /**
                       * @notice Validates the health factor of a user.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The state of the user for the specific reserve
                       * @param user The user to validate health factor of
                       * @param userEModeCategory The users active efficiency mode category
                       * @param reservesCount The number of available reserves
                       * @param oracle The price oracle
                       */
                      function validateHealthFactor(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap memory userConfig,
                        address user,
                        uint8 userEModeCategory,
                        uint256 reservesCount,
                        address oracle
                      ) internal view returns (uint256, bool) {
                        (, , , , uint256 healthFactor, bool hasZeroLtvCollateral) = GenericLogic
                          .calculateUserAccountData(
                            reservesData,
                            reservesList,
                            eModeCategories,
                            DataTypes.CalculateUserAccountDataParams({
                              userConfig: userConfig,
                              reservesCount: reservesCount,
                              user: user,
                              oracle: oracle,
                              userEModeCategory: userEModeCategory
                            })
                          );
                        require(
                          healthFactor >= HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                          Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                        );
                        return (healthFactor, hasZeroLtvCollateral);
                      }
                      /**
                       * @notice Validates the health factor of a user and the ltv of the asset being withdrawn.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The state of the user for the specific reserve
                       * @param asset The asset for which the ltv will be validated
                       * @param from The user from which the aTokens are being transferred
                       * @param reservesCount The number of available reserves
                       * @param oracle The price oracle
                       * @param userEModeCategory The users active efficiency mode category
                       */
                      function validateHFAndLtv(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap memory userConfig,
                        address asset,
                        address from,
                        uint256 reservesCount,
                        address oracle,
                        uint8 userEModeCategory
                      ) internal view {
                        DataTypes.ReserveData memory reserve = reservesData[asset];
                        (, bool hasZeroLtvCollateral) = validateHealthFactor(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          userConfig,
                          from,
                          userEModeCategory,
                          reservesCount,
                          oracle
                        );
                        require(
                          !hasZeroLtvCollateral || reserve.configuration.getLtv() == 0,
                          Errors.LTV_VALIDATION_FAILED
                        );
                      }
                      /**
                       * @notice Validates a transfer action.
                       * @param reserve The reserve object
                       */
                      function validateTransfer(DataTypes.ReserveData storage reserve) internal view {
                        require(!reserve.configuration.getPaused(), Errors.RESERVE_PAUSED);
                      }
                      /**
                       * @notice Validates a drop reserve action.
                       * @param reservesList The addresses of all the active reserves
                       * @param reserve The reserve object
                       * @param asset The address of the reserve's underlying asset
                       */
                      function validateDropReserve(
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.ReserveData storage reserve,
                        address asset
                      ) internal view {
                        require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                        require(reserve.id != 0 || reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                        require(
                          IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0,
                          Errors.VARIABLE_DEBT_SUPPLY_NOT_ZERO
                        );
                        require(
                          IERC20(reserve.aTokenAddress).totalSupply() == 0 && reserve.accruedToTreasury == 0,
                          Errors.UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO
                        );
                      }
                      /**
                       * @notice Validates the action of setting efficiency mode.
                       * @param eModeCategories a mapping storing configurations for all efficiency mode categories
                       * @param userConfig the user configuration
                       * @param reservesCount The total number of valid reserves
                       * @param categoryId The id of the category
                       */
                      function validateSetUserEMode(
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap memory userConfig,
                        uint256 reservesCount,
                        uint8 categoryId
                      ) internal view {
                        DataTypes.EModeCategory storage eModeCategory = eModeCategories[categoryId];
                        // category is invalid if the liq threshold is not set
                        require(
                          categoryId == 0 || eModeCategory.liquidationThreshold != 0,
                          Errors.INCONSISTENT_EMODE_CATEGORY
                        );
                        // eMode can always be enabled if the user hasn't supplied anything
                        if (userConfig.isEmpty()) {
                          return;
                        }
                        // if user is trying to set another category than default we require that
                        // either the user is not borrowing, or it's borrowing assets of categoryId
                        if (categoryId != 0) {
                          unchecked {
                            for (uint256 i = 0; i < reservesCount; i++) {
                              if (userConfig.isBorrowing(i)) {
                                require(
                                  EModeConfiguration.isReserveEnabledOnBitmap(eModeCategory.borrowableBitmap, i),
                                  Errors.NOT_BORROWABLE_IN_EMODE
                                );
                              }
                            }
                          }
                        }
                      }
                      /**
                       * @notice Validates the action of activating the asset as collateral.
                       * @dev Only possible if the asset has non-zero LTV and the user is not in isolation mode
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig the user configuration
                       * @param reserveConfig The reserve configuration
                       * @return True if the asset can be activated as collateral, false otherwise
                       */
                      function validateUseAsCollateral(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ReserveConfigurationMap memory reserveConfig
                      ) internal view returns (bool) {
                        if (reserveConfig.getLtv() == 0) {
                          return false;
                        }
                        if (!userConfig.isUsingAsCollateralAny()) {
                          return true;
                        }
                        (bool isolationModeActive, , ) = userConfig.getIsolationModeState(reservesData, reservesList);
                        return (!isolationModeActive && reserveConfig.getDebtCeiling() == 0);
                      }
                      /**
                       * @notice Validates if an asset should be automatically activated as collateral in the following actions: supply,
                       * transfer, mint unbacked, and liquidate
                       * @dev This is used to ensure that isolated assets are not enabled as collateral automatically
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig the user configuration
                       * @param reserveConfig The reserve configuration
                       * @return True if the asset can be activated as collateral, false otherwise
                       */
                      function validateAutomaticUseAsCollateral(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ReserveConfigurationMap memory reserveConfig,
                        address aTokenAddress
                      ) internal view returns (bool) {
                        if (reserveConfig.getDebtCeiling() != 0) {
                          // ensures only the ISOLATED_COLLATERAL_SUPPLIER_ROLE can enable collateral as side-effect of an action
                          IPoolAddressesProvider addressesProvider = IncentivizedERC20(aTokenAddress)
                            .POOL()
                            .ADDRESSES_PROVIDER();
                          if (
                            !IAccessControl(addressesProvider.getACLManager()).hasRole(
                              ISOLATED_COLLATERAL_SUPPLIER_ROLE,
                              msg.sender
                            )
                          ) return false;
                        }
                        return validateUseAsCollateral(reservesData, reservesList, userConfig, reserveConfig);
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                    import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {EModeLogic} from './EModeLogic.sol';
                    /**
                     * @title GenericLogic library
                     * @author Aave
                     * @notice Implements protocol-level logic to calculate and validate the state of a user
                     */
                    library GenericLogic {
                      using ReserveLogic for DataTypes.ReserveData;
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      struct CalculateUserAccountDataVars {
                        uint256 assetPrice;
                        uint256 assetUnit;
                        uint256 userBalanceInBaseCurrency;
                        uint256 decimals;
                        uint256 ltv;
                        uint256 liquidationThreshold;
                        uint256 i;
                        uint256 healthFactor;
                        uint256 totalCollateralInBaseCurrency;
                        uint256 totalDebtInBaseCurrency;
                        uint256 avgLtv;
                        uint256 avgLiquidationThreshold;
                        uint256 eModeLtv;
                        uint256 eModeLiqThreshold;
                        address currentReserveAddress;
                        bool hasZeroLtvCollateral;
                        bool isInEModeCategory;
                      }
                      /**
                       * @notice Calculates the user data across the reserves.
                       * @dev It includes the total liquidity/collateral/borrow balances in the base currency used by the price feed,
                       * the average Loan To Value, the average Liquidation Ratio, and the Health factor.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param params Additional parameters needed for the calculation
                       * @return The total collateral of the user in the base currency used by the price feed
                       * @return The total debt of the user in the base currency used by the price feed
                       * @return The average ltv of the user
                       * @return The average liquidation threshold of the user
                       * @return The health factor of the user
                       * @return True if the ltv is zero, false otherwise
                       */
                      function calculateUserAccountData(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.CalculateUserAccountDataParams memory params
                      ) internal view returns (uint256, uint256, uint256, uint256, uint256, bool) {
                        if (params.userConfig.isEmpty()) {
                          return (0, 0, 0, 0, type(uint256).max, false);
                        }
                        CalculateUserAccountDataVars memory vars;
                        if (params.userEModeCategory != 0) {
                          vars.eModeLtv = eModeCategories[params.userEModeCategory].ltv;
                          vars.eModeLiqThreshold = eModeCategories[params.userEModeCategory].liquidationThreshold;
                        }
                        while (vars.i < params.reservesCount) {
                          if (!params.userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
                            unchecked {
                              ++vars.i;
                            }
                            continue;
                          }
                          vars.currentReserveAddress = reservesList[vars.i];
                          if (vars.currentReserveAddress == address(0)) {
                            unchecked {
                              ++vars.i;
                            }
                            continue;
                          }
                          DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
                          (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve
                            .configuration
                            .getParams();
                          unchecked {
                            vars.assetUnit = 10 ** vars.decimals;
                          }
                          vars.assetPrice = IPriceOracleGetter(params.oracle).getAssetPrice(vars.currentReserveAddress);
                          if (vars.liquidationThreshold != 0 && params.userConfig.isUsingAsCollateral(vars.i)) {
                            vars.userBalanceInBaseCurrency = _getUserBalanceInBaseCurrency(
                              params.user,
                              currentReserve,
                              vars.assetPrice,
                              vars.assetUnit
                            );
                            vars.totalCollateralInBaseCurrency += vars.userBalanceInBaseCurrency;
                            vars.isInEModeCategory =
                              params.userEModeCategory != 0 &&
                              EModeConfiguration.isReserveEnabledOnBitmap(
                                eModeCategories[params.userEModeCategory].collateralBitmap,
                                vars.i
                              );
                            if (vars.ltv != 0) {
                              vars.avgLtv +=
                                vars.userBalanceInBaseCurrency *
                                (vars.isInEModeCategory ? vars.eModeLtv : vars.ltv);
                            } else {
                              vars.hasZeroLtvCollateral = true;
                            }
                            vars.avgLiquidationThreshold +=
                              vars.userBalanceInBaseCurrency *
                              (vars.isInEModeCategory ? vars.eModeLiqThreshold : vars.liquidationThreshold);
                          }
                          if (params.userConfig.isBorrowing(vars.i)) {
                            vars.totalDebtInBaseCurrency += _getUserDebtInBaseCurrency(
                              params.user,
                              currentReserve,
                              vars.assetPrice,
                              vars.assetUnit
                            );
                          }
                          unchecked {
                            ++vars.i;
                          }
                        }
                        unchecked {
                          vars.avgLtv = vars.totalCollateralInBaseCurrency != 0
                            ? vars.avgLtv / vars.totalCollateralInBaseCurrency
                            : 0;
                          vars.avgLiquidationThreshold = vars.totalCollateralInBaseCurrency != 0
                            ? vars.avgLiquidationThreshold / vars.totalCollateralInBaseCurrency
                            : 0;
                        }
                        vars.healthFactor = (vars.totalDebtInBaseCurrency == 0)
                          ? type(uint256).max
                          : (vars.totalCollateralInBaseCurrency.percentMul(vars.avgLiquidationThreshold)).wadDiv(
                            vars.totalDebtInBaseCurrency
                          );
                        return (
                          vars.totalCollateralInBaseCurrency,
                          vars.totalDebtInBaseCurrency,
                          vars.avgLtv,
                          vars.avgLiquidationThreshold,
                          vars.healthFactor,
                          vars.hasZeroLtvCollateral
                        );
                      }
                      /**
                       * @notice Calculates the maximum amount that can be borrowed depending on the available collateral, the total debt
                       * and the average Loan To Value
                       * @param totalCollateralInBaseCurrency The total collateral in the base currency used by the price feed
                       * @param totalDebtInBaseCurrency The total borrow balance in the base currency used by the price feed
                       * @param ltv The average loan to value
                       * @return The amount available to borrow in the base currency of the used by the price feed
                       */
                      function calculateAvailableBorrows(
                        uint256 totalCollateralInBaseCurrency,
                        uint256 totalDebtInBaseCurrency,
                        uint256 ltv
                      ) internal pure returns (uint256) {
                        uint256 availableBorrowsInBaseCurrency = totalCollateralInBaseCurrency.percentMul(ltv);
                        if (availableBorrowsInBaseCurrency <= totalDebtInBaseCurrency) {
                          return 0;
                        }
                        availableBorrowsInBaseCurrency = availableBorrowsInBaseCurrency - totalDebtInBaseCurrency;
                        return availableBorrowsInBaseCurrency;
                      }
                      /**
                       * @notice Calculates total debt of the user in the based currency used to normalize the values of the assets
                       * @dev This fetches the `balanceOf` of the variable debt token for the user. For gas reasons, the
                       * variable debt balance is calculated by fetching `scaledBalancesOf` normalized debt, which is cheaper than
                       * fetching `balanceOf`
                       * @param user The address of the user
                       * @param reserve The data of the reserve for which the total debt of the user is being calculated
                       * @param assetPrice The price of the asset for which the total debt of the user is being calculated
                       * @param assetUnit The value representing one full unit of the asset (10^decimals)
                       * @return The total debt of the user normalized to the base currency
                       */
                      function _getUserDebtInBaseCurrency(
                        address user,
                        DataTypes.ReserveData storage reserve,
                        uint256 assetPrice,
                        uint256 assetUnit
                      ) private view returns (uint256) {
                        // fetching variable debt
                        uint256 userTotalDebt = IScaledBalanceToken(reserve.variableDebtTokenAddress).scaledBalanceOf(
                          user
                        );
                        if (userTotalDebt == 0) {
                          return 0;
                        }
                        userTotalDebt = userTotalDebt.rayMul(reserve.getNormalizedDebt()) * assetPrice;
                        unchecked {
                          return userTotalDebt / assetUnit;
                        }
                      }
                      /**
                       * @notice Calculates total aToken balance of the user in the based currency used by the price oracle
                       * @dev For gas reasons, the aToken balance is calculated by fetching `scaledBalancesOf` normalized debt, which
                       * is cheaper than fetching `balanceOf`
                       * @param user The address of the user
                       * @param reserve The data of the reserve for which the total aToken balance of the user is being calculated
                       * @param assetPrice The price of the asset for which the total aToken balance of the user is being calculated
                       * @param assetUnit The value representing one full unit of the asset (10^decimals)
                       * @return The total aToken balance of the user normalized to the base currency of the price oracle
                       */
                      function _getUserBalanceInBaseCurrency(
                        address user,
                        DataTypes.ReserveData storage reserve,
                        uint256 assetPrice,
                        uint256 assetUnit
                      ) private view returns (uint256) {
                        uint256 normalizedIncome = reserve.getNormalizedIncome();
                        uint256 balance = (
                          IScaledBalanceToken(reserve.aTokenAddress).scaledBalanceOf(user).rayMul(normalizedIncome)
                        ) * assetPrice;
                        unchecked {
                          return balance / assetUnit;
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                    import {IInitializableDebtToken} from './IInitializableDebtToken.sol';
                    /**
                     * @title IVariableDebtToken
                     * @author Aave
                     * @notice Defines the basic interface for a variable debt token.
                     */
                    interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken {
                      /**
                       * @notice 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 previous balance of the user is 0, false otherwise
                       * @return The scaled total debt of the reserve
                       */
                      function mint(
                        address user,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) external returns (bool, uint256);
                      /**
                       * @notice Burns user variable debt
                       * @dev In some instances, a burn transaction will emit a mint event
                       * if the amount to burn is less than the interest that the user accrued
                       * @param from The address from which the debt will be burned
                       * @param amount The amount getting burned
                       * @param index The variable debt index of the reserve
                       * @return The scaled total debt of the reserve
                       */
                      function burn(address from, uint256 amount, uint256 index) external returns (uint256);
                      /**
                       * @notice Returns the address of the underlying asset of this debtToken (E.g. WETH for variableDebtWETH)
                       * @return The address of the underlying asset
                       */
                      function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                    /**
                     * @title IReserveInterestRateStrategy
                     * @author BGD Labs
                     * @notice Basic interface for any rate strategy used by the Aave protocol
                     */
                    interface IReserveInterestRateStrategy {
                      /**
                       * @notice Sets interest rate data for an Aave rate strategy
                       * @param reserve The reserve to update
                       * @param rateData The abi encoded reserve interest rate data to apply to the given reserve
                       *   Abstracted this way as rate strategies can be custom
                       */
                      function setInterestRateParams(address reserve, bytes calldata rateData) external;
                      /**
                       * @notice Calculates the interest rates depending on the reserve's state and configurations
                       * @param params The parameters needed to calculate interest rates
                       * @return liquidityRate The liquidity rate expressed in ray
                       * @return variableBorrowRate The variable borrow rate expressed in ray
                       */
                      function calculateInterestRates(
                        DataTypes.CalculateInterestRatesParams memory params
                      ) external view returns (uint256, uint256);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    import {WadRayMath} from './WadRayMath.sol';
                    /**
                     * @title MathUtils library
                     * @author Aave
                     * @notice Provides functions to perform linear and compounded interest calculations
                     */
                    library MathUtils {
                      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 result = rate * (block.timestamp - uint256(lastUpdateTimestamp));
                        unchecked {
                          result = result / SECONDS_PER_YEAR;
                        }
                        return WadRayMath.RAY + result;
                      }
                      /**
                       * @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 - uint256(lastUpdateTimestamp);
                        if (exp == 0) {
                          return WadRayMath.RAY;
                        }
                        uint256 expMinusOne;
                        uint256 expMinusTwo;
                        uint256 basePowerTwo;
                        uint256 basePowerThree;
                        unchecked {
                          expMinusOne = exp - 1;
                          expMinusTwo = exp > 2 ? exp - 2 : 0;
                          basePowerTwo = rate.rayMul(rate) / (SECONDS_PER_YEAR * SECONDS_PER_YEAR);
                          basePowerThree = basePowerTwo.rayMul(rate) / SECONDS_PER_YEAR;
                        }
                        uint256 secondTerm = exp * expMinusOne * basePowerTwo;
                        unchecked {
                          secondTerm /= 2;
                        }
                        uint256 thirdTerm = exp * expMinusOne * expMinusTwo * basePowerThree;
                        unchecked {
                          thirdTerm /= 6;
                        }
                        return WadRayMath.RAY + (rate * exp) / SECONDS_PER_YEAR + secondTerm + 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
                       * @return The interest rate compounded between lastUpdateTimestamp and current block timestamp, in ray
                       */
                      function calculateCompoundedInterest(
                        uint256 rate,
                        uint40 lastUpdateTimestamp
                      ) internal view returns (uint256) {
                        return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp);
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @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. If a value is >=.5, will be rounded up, otherwise rounded down.
                     */
                    library PercentageMath {
                      // Maximum percentage factor (100.00%)
                      uint256 internal constant PERCENTAGE_FACTOR = 1e4;
                      // Half percentage factor (50.00%)
                      uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4;
                      /**
                       * @notice Executes a percentage multiplication
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param value The value of which the percentage needs to be calculated
                       * @param percentage The percentage of the value to be calculated
                       * @return result value percentmul percentage
                       */
                      function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                        // to avoid overflow, value <= (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentage
                        assembly {
                          if iszero(
                            or(
                              iszero(percentage),
                              iszero(gt(value, div(sub(not(0), HALF_PERCENTAGE_FACTOR), percentage)))
                            )
                          ) {
                            revert(0, 0)
                          }
                          result := div(add(mul(value, percentage), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
                        }
                      }
                      /**
                       * @notice Executes a percentage division
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param value The value of which the percentage needs to be calculated
                       * @param percentage The percentage of the value to be calculated
                       * @return result value percentdiv percentage
                       */
                      function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                        // to avoid overflow, value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR
                        assembly {
                          if or(
                            iszero(percentage),
                            iszero(iszero(gt(value, div(sub(not(0), div(percentage, 2)), PERCENTAGE_FACTOR))))
                          ) {
                            revert(0, 0)
                          }
                          result := div(add(mul(value, PERCENTAGE_FACTOR), div(percentage, 2)), percentage)
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                    pragma solidity ^0.8.10;
                    /**
                     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                     * checks.
                     *
                     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                     * easily result in undesired exploitation or bugs, since developers usually
                     * assume that overflows raise errors. `SafeCast` restores this intuition by
                     * reverting the transaction when such 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.
                     *
                     * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                     * all math on `uint256` and `int256` and then downcasting.
                     */
                    library SafeCast {
                      /**
                       * @dev Returns the downcasted uint224 from uint256, reverting on
                       * overflow (when the input is greater than largest uint224).
                       *
                       * Counterpart to Solidity's `uint224` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 224 bits
                       */
                      function toUint224(uint256 value) internal pure returns (uint224) {
                        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                        return uint224(value);
                      }
                      /**
                       * @dev Returns the downcasted uint128 from uint256, reverting on
                       * overflow (when the input is greater than largest uint128).
                       *
                       * Counterpart to Solidity's `uint128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       */
                      function toUint128(uint256 value) internal pure returns (uint128) {
                        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                        return uint128(value);
                      }
                      /**
                       * @dev Returns the downcasted uint96 from uint256, reverting on
                       * overflow (when the input is greater than largest uint96).
                       *
                       * Counterpart to Solidity's `uint96` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 96 bits
                       */
                      function toUint96(uint256 value) internal pure returns (uint96) {
                        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                        return uint96(value);
                      }
                      /**
                       * @dev Returns the downcasted uint64 from uint256, reverting on
                       * overflow (when the input is greater than largest uint64).
                       *
                       * Counterpart to Solidity's `uint64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       */
                      function toUint64(uint256 value) internal pure returns (uint64) {
                        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                        return uint64(value);
                      }
                      /**
                       * @dev Returns the downcasted uint32 from uint256, reverting on
                       * overflow (when the input is greater than largest uint32).
                       *
                       * Counterpart to Solidity's `uint32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       */
                      function toUint32(uint256 value) internal pure returns (uint32) {
                        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                        return uint32(value);
                      }
                      /**
                       * @dev Returns the downcasted uint16 from uint256, reverting on
                       * overflow (when the input is greater than largest uint16).
                       *
                       * Counterpart to Solidity's `uint16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       */
                      function toUint16(uint256 value) internal pure returns (uint16) {
                        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                        return uint16(value);
                      }
                      /**
                       * @dev Returns the downcasted uint8 from uint256, reverting on
                       * overflow (when the input is greater than largest uint8).
                       *
                       * Counterpart to Solidity's `uint8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       */
                      function toUint8(uint256 value) internal pure returns (uint8) {
                        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                        return uint8(value);
                      }
                      /**
                       * @dev Converts a signed int256 into an unsigned uint256.
                       *
                       * Requirements:
                       *
                       * - input must be greater than or equal to 0.
                       */
                      function toUint256(int256 value) internal pure returns (uint256) {
                        require(value >= 0, 'SafeCast: value must be positive');
                        return uint256(value);
                      }
                      /**
                       * @dev Returns the downcasted int128 from int256, reverting on
                       * overflow (when the input is less than smallest int128 or
                       * greater than largest int128).
                       *
                       * Counterpart to Solidity's `int128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt128(int256 value) internal pure returns (int128) {
                        require(
                          value >= type(int128).min && value <= type(int128).max,
                          "SafeCast: value doesn't fit in 128 bits"
                        );
                        return int128(value);
                      }
                      /**
                       * @dev Returns the downcasted int64 from int256, reverting on
                       * overflow (when the input is less than smallest int64 or
                       * greater than largest int64).
                       *
                       * Counterpart to Solidity's `int64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt64(int256 value) internal pure returns (int64) {
                        require(
                          value >= type(int64).min && value <= type(int64).max,
                          "SafeCast: value doesn't fit in 64 bits"
                        );
                        return int64(value);
                      }
                      /**
                       * @dev Returns the downcasted int32 from int256, reverting on
                       * overflow (when the input is less than smallest int32 or
                       * greater than largest int32).
                       *
                       * Counterpart to Solidity's `int32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt32(int256 value) internal pure returns (int32) {
                        require(
                          value >= type(int32).min && value <= type(int32).max,
                          "SafeCast: value doesn't fit in 32 bits"
                        );
                        return int32(value);
                      }
                      /**
                       * @dev Returns the downcasted int16 from int256, reverting on
                       * overflow (when the input is less than smallest int16 or
                       * greater than largest int16).
                       *
                       * Counterpart to Solidity's `int16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt16(int256 value) internal pure returns (int16) {
                        require(
                          value >= type(int16).min && value <= type(int16).max,
                          "SafeCast: value doesn't fit in 16 bits"
                        );
                        return int16(value);
                      }
                      /**
                       * @dev Returns the downcasted int8 from int256, reverting on
                       * overflow (when the input is less than smallest int8 or
                       * greater than largest int8).
                       *
                       * Counterpart to Solidity's `int8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       *
                       * _Available since v3.1._
                       */
                      function toInt8(int256 value) internal pure returns (int8) {
                        require(
                          value >= type(int8).min && value <= type(int8).max,
                          "SafeCast: value doesn't fit in 8 bits"
                        );
                        return int8(value);
                      }
                      /**
                       * @dev Converts an unsigned uint256 into a signed int256.
                       *
                       * Requirements:
                       *
                       * - input must be less than or equal to maxInt256.
                       */
                      function toInt256(uint256 value) internal pure returns (int256) {
                        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                        return int256(value);
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IPriceOracleGetter
                     * @author Aave
                     * @notice Interface for the Aave price oracle.
                     */
                    interface IPriceOracleGetter {
                      /**
                       * @notice Returns the base currency address
                       * @dev Address 0x0 is reserved for USD as base currency.
                       * @return Returns the base currency address.
                       */
                      function BASE_CURRENCY() external view returns (address);
                      /**
                       * @notice Returns the base currency unit
                       * @dev 1 ether for ETH, 1e8 for USD.
                       * @return Returns the base currency unit.
                       */
                      function BASE_CURRENCY_UNIT() external view returns (uint256);
                      /**
                       * @notice Returns the asset price in the base currency
                       * @param asset The address of the asset
                       * @return The price of the asset
                       */
                      function getAssetPrice(address asset) external view returns (uint256);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {Errors} from '../helpers/Errors.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveConfiguration} from './ReserveConfiguration.sol';
                    /**
                     * @title UserConfiguration library
                     * @author Aave
                     * @notice Implements the bitmap logic to handle the user configuration
                     */
                    library UserConfiguration {
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      uint256 internal constant BORROWING_MASK =
                        0x5555555555555555555555555555555555555555555555555555555555555555;
                      uint256 internal constant COLLATERAL_MASK =
                        0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;
                      /**
                       * @notice 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 {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          uint256 bit = 1 << (reserveIndex << 1);
                          if (borrowing) {
                            self.data |= bit;
                          } else {
                            self.data &= ~bit;
                          }
                        }
                      }
                      /**
                       * @notice 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 using the reserve as collateral, false otherwise
                       */
                      function setUsingAsCollateral(
                        DataTypes.UserConfigurationMap storage self,
                        uint256 reserveIndex,
                        bool usingAsCollateral
                      ) internal {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          uint256 bit = 1 << ((reserveIndex << 1) + 1);
                          if (usingAsCollateral) {
                            self.data |= bit;
                          } else {
                            self.data &= ~bit;
                          }
                        }
                      }
                      /**
                       * @notice Returns 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) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          return (self.data >> (reserveIndex << 1)) & 3 != 0;
                        }
                      }
                      /**
                       * @notice Validate 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) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          return (self.data >> (reserveIndex << 1)) & 1 != 0;
                        }
                      }
                      /**
                       * @notice Validate 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) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0;
                        }
                      }
                      /**
                       * @notice Checks if a user has been supplying only one reserve as collateral
                       * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0
                       * @param self The configuration object
                       * @return True if the user has been supplying as collateral one reserve, false otherwise
                       */
                      function isUsingAsCollateralOne(
                        DataTypes.UserConfigurationMap memory self
                      ) internal pure returns (bool) {
                        uint256 collateralData = self.data & COLLATERAL_MASK;
                        return collateralData != 0 && (collateralData & (collateralData - 1) == 0);
                      }
                      /**
                       * @notice Checks if a user has been supplying any reserve as collateral
                       * @param self The configuration object
                       * @return True if the user has been supplying as collateral any reserve, false otherwise
                       */
                      function isUsingAsCollateralAny(
                        DataTypes.UserConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return self.data & COLLATERAL_MASK != 0;
                      }
                      /**
                       * @notice Checks if a user has been borrowing only one asset
                       * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0
                       * @param self The configuration object
                       * @return True if the user has been supplying as collateral one reserve, false otherwise
                       */
                      function isBorrowingOne(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                        uint256 borrowingData = self.data & BORROWING_MASK;
                        return borrowingData != 0 && (borrowingData & (borrowingData - 1) == 0);
                      }
                      /**
                       * @notice Checks 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;
                      }
                      /**
                       * @notice Checks if a user has not been using any reserve for borrowing or supply
                       * @param self The configuration object
                       * @return True if the user has not been borrowing or supplying any reserve, false otherwise
                       */
                      function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                        return self.data == 0;
                      }
                      /**
                       * @notice Returns the Isolation Mode state of the user
                       * @param self The configuration object
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @return True if the user is in isolation mode, false otherwise
                       * @return The address of the only asset used as collateral
                       * @return The debt ceiling of the reserve
                       */
                      function getIsolationModeState(
                        DataTypes.UserConfigurationMap memory self,
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList
                      ) internal view returns (bool, address, uint256) {
                        if (isUsingAsCollateralOne(self)) {
                          uint256 assetId = _getFirstAssetIdByMask(self, COLLATERAL_MASK);
                          address assetAddress = reservesList[assetId];
                          uint256 ceiling = reservesData[assetAddress].configuration.getDebtCeiling();
                          if (ceiling != 0) {
                            return (true, assetAddress, ceiling);
                          }
                        }
                        return (false, address(0), 0);
                      }
                      /**
                       * @notice Returns the siloed borrowing state for the user
                       * @param self The configuration object
                       * @param reservesData The data of all the reserves
                       * @param reservesList The reserve list
                       * @return True if the user has borrowed a siloed asset, false otherwise
                       * @return The address of the only borrowed asset
                       */
                      function getSiloedBorrowingState(
                        DataTypes.UserConfigurationMap memory self,
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList
                      ) internal view returns (bool, address) {
                        if (isBorrowingOne(self)) {
                          uint256 assetId = _getFirstAssetIdByMask(self, BORROWING_MASK);
                          address assetAddress = reservesList[assetId];
                          if (reservesData[assetAddress].configuration.getSiloedBorrowing()) {
                            return (true, assetAddress);
                          }
                        }
                        return (false, address(0));
                      }
                      /**
                       * @notice Returns the address of the first asset flagged in the bitmap given the corresponding bitmask
                       * @param self The configuration object
                       * @return The index of the first asset flagged in the bitmap once the corresponding mask is applied
                       */
                      function _getFirstAssetIdByMask(
                        DataTypes.UserConfigurationMap memory self,
                        uint256 mask
                      ) internal pure returns (uint256) {
                        unchecked {
                          uint256 bitmapData = self.data & mask;
                          uint256 firstAssetPosition = bitmapData & ~(bitmapData - 1);
                          uint256 id;
                          while ((firstAssetPosition >>= 2) != 0) {
                            id += 1;
                          }
                          return id;
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    /**
                     * @title IFlashLoanReceiver
                     * @author Aave
                     * @notice Defines the basic interface of a flashloan-receiver contract.
                     * @dev Implement this interface to develop a flashloan-compatible flashLoanReceiver contract
                     */
                    interface IFlashLoanReceiver {
                      /**
                       * @notice Executes an operation after receiving the flash-borrowed assets
                       * @dev Ensure that the contract can return the debt + premium, e.g., has
                       *      enough funds to repay and has approved the Pool to pull the total amount
                       * @param assets The addresses of the flash-borrowed assets
                       * @param amounts The amounts of the flash-borrowed assets
                       * @param premiums The fee of each flash-borrowed asset
                       * @param initiator The address of the flashloan initiator
                       * @param params The byte-encoded params passed when initiating the flashloan
                       * @return True if the execution of the operation succeeds, false otherwise
                       */
                      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 (IPoolAddressesProvider);
                      function POOL() external view returns (IPool);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    /**
                     * @title IFlashLoanSimpleReceiver
                     * @author Aave
                     * @notice Defines the basic interface of a flashloan-receiver contract.
                     * @dev Implement this interface to develop a flashloan-compatible flashLoanReceiver contract
                     */
                    interface IFlashLoanSimpleReceiver {
                      /**
                       * @notice Executes an operation after receiving the flash-borrowed asset
                       * @dev Ensure that the contract can return the debt + premium, e.g., has
                       *      enough funds to repay and has approved the Pool to pull the total amount
                       * @param asset The address of the flash-borrowed asset
                       * @param amount The amount of the flash-borrowed asset
                       * @param premium The fee of the flash-borrowed asset
                       * @param initiator The address of the flashloan initiator
                       * @param params The byte-encoded params passed when initiating the flashloan
                       * @return True if the execution of the operation succeeds, false otherwise
                       */
                      function executeOperation(
                        address asset,
                        uint256 amount,
                        uint256 premium,
                        address initiator,
                        bytes calldata params
                      ) external returns (bool);
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      function POOL() external view returns (IPool);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    /**
                     * @title IsolationModeLogic library
                     * @author Aave
                     * @notice Implements the base logic for handling repayments for assets borrowed in isolation mode
                     */
                    library IsolationModeLogic {
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using SafeCast for uint256;
                      // See `IPool` for descriptions
                      event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                      /**
                       * @notice updated the isolated debt whenever a position collateralized by an isolated asset is repaid or liquidated
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig The user configuration mapping
                       * @param reserveCache The cached data of the reserve
                       * @param repayAmount The amount being repaid
                       */
                      function updateIsolatedDebtIfIsolated(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ReserveCache memory reserveCache,
                        uint256 repayAmount
                      ) internal {
                        (bool isolationModeActive, address isolationModeCollateralAddress, ) = userConfig
                          .getIsolationModeState(reservesData, reservesList);
                        if (isolationModeActive) {
                          uint128 isolationModeTotalDebt = reservesData[isolationModeCollateralAddress]
                            .isolationModeTotalDebt;
                          uint128 isolatedDebtRepaid = (repayAmount /
                            10 **
                              (reserveCache.reserveConfiguration.getDecimals() -
                                ReserveConfiguration.DEBT_CEILING_DECIMALS)).toUint128();
                          // since the debt ceiling does not take into account the interest accrued, it might happen that amount
                          // repaid > debt in isolation mode
                          if (isolationModeTotalDebt <= isolatedDebtRepaid) {
                            reservesData[isolationModeCollateralAddress].isolationModeTotalDebt = 0;
                            emit IsolationModeTotalDebtUpdated(isolationModeCollateralAddress, 0);
                          } else {
                            uint256 nextIsolationModeTotalDebt = reservesData[isolationModeCollateralAddress]
                              .isolationModeTotalDebt = isolationModeTotalDebt - isolatedDebtRepaid;
                            emit IsolationModeTotalDebtUpdated(
                              isolationModeCollateralAddress,
                              nextIsolationModeTotalDebt
                            );
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {Errors} from '../helpers/Errors.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveConfiguration} from './ReserveConfiguration.sol';
                    /**
                     * @title EModeConfiguration library
                     * @author BGD Labs
                     * @notice Implements the bitmap logic to handle the eMode configuration
                     */
                    library EModeConfiguration {
                      /**
                       * @notice Sets a bit in a given bitmap that represents the reserve index range
                       * @dev The supplied bitmap is supposed to be a uint128 in which each bit represents a reserve
                       * @param bitmap The bitmap
                       * @param reserveIndex The index of the reserve in the bitmap
                       * @param enabled True if the reserveIndex should be enabled on the bitmap, false otherwise
                       * @return The altered bitmap
                       */
                      function setReserveBitmapBit(
                        uint128 bitmap,
                        uint256 reserveIndex,
                        bool enabled
                      ) internal pure returns (uint128) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          uint128 bit = uint128(1 << reserveIndex);
                          if (enabled) {
                            return bitmap | bit;
                          } else {
                            return bitmap & ~bit;
                          }
                        }
                      }
                      /**
                       * @notice Validates if a reserveIndex is flagged as enabled on a given bitmap
                       * @param bitmap The bitmap
                       * @param reserveIndex The index of the reserve in the bitmap
                       * @return True if the reserveindex is flagged true
                       */
                      function isReserveEnabledOnBitmap(
                        uint128 bitmap,
                        uint256 reserveIndex
                      ) internal pure returns (bool) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          return (bitmap >> reserveIndex) & 1 != 0;
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IScaledBalanceToken
                     * @author Aave
                     * @notice Defines the basic interface for a scaled-balance token.
                     */
                    interface IScaledBalanceToken {
                      /**
                       * @dev Emitted after the mint action
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the minted tokens
                       * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
                       * @param index The next liquidity index of the reserve
                       */
                      event Mint(
                        address indexed caller,
                        address indexed onBehalfOf,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @dev Emitted after the burn action
                       * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
                       * @param from The address from which the tokens will be burned
                       * @param target The address that will receive the underlying, if any
                       * @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
                       * @param index The next liquidity index of the reserve
                       */
                      event Burn(
                        address indexed from,
                        address indexed target,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @notice Returns the scaled balance of the user.
                       * @dev 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);
                      /**
                       * @notice 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 total supply
                       */
                      function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                      /**
                       * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
                       * @return The scaled total supply
                       */
                      function scaledTotalSupply() external view returns (uint256);
                      /**
                       * @notice Returns last index interest was accrued to the user's balance
                       * @param user The address of the user
                       * @return The last index interest was accrued to the user's balance, expressed in ray
                       */
                      function getPreviousIndex(address user) external view returns (uint256);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                    import {IPool} from './IPool.sol';
                    /**
                     * @title IInitializableAToken
                     * @author Aave
                     * @notice Interface for the initialize function on AToken
                     */
                    interface IInitializableAToken {
                      /**
                       * @dev Emitted when an aToken is initialized
                       * @param underlyingAsset The address of the underlying asset
                       * @param pool The address of the associated 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
                      );
                      /**
                       * @notice Initializes the aToken
                       * @param pool The pool contract that is initializing this contract
                       * @param treasury The address of the Aave treasury, receiving the fees on this aToken
                       * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @param incentivesController The smart contract managing potential incentives distribution
                       * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's
                       * @param aTokenName The name of the aToken
                       * @param aTokenSymbol The symbol of the aToken
                       * @param params A set of encoded parameters for additional initialization
                       */
                      function initialize(
                        IPool pool,
                        address treasury,
                        address underlyingAsset,
                        IAaveIncentivesController incentivesController,
                        uint8 aTokenDecimals,
                        string calldata aTokenName,
                        string calldata aTokenSymbol,
                        bytes calldata params
                      ) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    /**
                     * @title IPriceOracleSentinel
                     * @author Aave
                     * @notice Defines the basic interface for the PriceOracleSentinel
                     */
                    interface IPriceOracleSentinel {
                      /**
                       * @dev Emitted after the sequencer oracle is updated
                       * @param newSequencerOracle The new sequencer oracle
                       */
                      event SequencerOracleUpdated(address newSequencerOracle);
                      /**
                       * @dev Emitted after the grace period is updated
                       * @param newGracePeriod The new grace period value
                       */
                      event GracePeriodUpdated(uint256 newGracePeriod);
                      /**
                       * @notice Returns the PoolAddressesProvider
                       * @return The address of the PoolAddressesProvider contract
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Returns true if the `borrow` operation is allowed.
                       * @dev Operation not allowed when PriceOracle is down or grace period not passed.
                       * @return True if the `borrow` operation is allowed, false otherwise.
                       */
                      function isBorrowAllowed() external view returns (bool);
                      /**
                       * @notice Returns true if the `liquidation` operation is allowed.
                       * @dev Operation not allowed when PriceOracle is down or grace period not passed.
                       * @return True if the `liquidation` operation is allowed, false otherwise.
                       */
                      function isLiquidationAllowed() external view returns (bool);
                      /**
                       * @notice Updates the address of the sequencer oracle
                       * @param newSequencerOracle The address of the new Sequencer Oracle to use
                       */
                      function setSequencerOracle(address newSequencerOracle) external;
                      /**
                       * @notice Updates the duration of the grace period
                       * @param newGracePeriod The value of the new grace period duration
                       */
                      function setGracePeriod(uint256 newGracePeriod) external;
                      /**
                       * @notice Returns the SequencerOracle
                       * @return The address of the sequencer oracle contract
                       */
                      function getSequencerOracle() external view returns (address);
                      /**
                       * @notice Returns the grace period
                       * @return The duration of the grace period
                       */
                      function getGracePeriod() external view returns (uint256);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.10;
                    /**
                     * @dev External interface of AccessControl declared to support ERC165 detection.
                     */
                    interface IAccessControl {
                      /**
                       * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                       *
                       * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                       * {RoleAdminChanged} not being emitted signaling this.
                       *
                       * _Available since v3.1._
                       */
                      event RoleAdminChanged(
                        bytes32 indexed role,
                        bytes32 indexed previousAdminRole,
                        bytes32 indexed newAdminRole
                      );
                      /**
                       * @dev Emitted when `account` is granted `role`.
                       *
                       * `sender` is the account that originated the contract call, an admin role
                       * bearer except when using {AccessControl-_setupRole}.
                       */
                      event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Emitted when `account` is revoked `role`.
                       *
                       * `sender` is the account that originated the contract call:
                       *   - if using `revokeRole`, it is the admin role bearer
                       *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                       */
                      event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Returns `true` if `account` has been granted `role`.
                       */
                      function hasRole(bytes32 role, address account) external view returns (bool);
                      /**
                       * @dev Returns the admin role that controls `role`. See {grantRole} and
                       * {revokeRole}.
                       *
                       * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                       */
                      function getRoleAdmin(bytes32 role) external view returns (bytes32);
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function grantRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * If `account` had been granted `role`, emits a {RoleRevoked} event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function revokeRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from the calling account.
                       *
                       * Roles are often managed via {grantRole} and {revokeRole}: this function's
                       * purpose is to provide a mechanism for accounts to lose their privileges
                       * if they are compromised (such as when a trusted device is misplaced).
                       *
                       * If the calling account had been granted `role`, emits a {RoleRevoked}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must be `account`.
                       */
                      function renounceRole(bytes32 role, address account) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.10;
                    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 {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                    import {Errors} from '../../libraries/helpers/Errors.sol';
                    import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IACLManager} from '../../../interfaces/IACLManager.sol';
                    /**
                     * @title IncentivizedERC20
                     * @author Aave, inspired by the Openzeppelin ERC20 implementation
                     * @notice Basic ERC20 implementation
                     */
                    abstract contract IncentivizedERC20 is Context, IERC20Detailed {
                      using WadRayMath for uint256;
                      using SafeCast for uint256;
                      /**
                       * @dev Only pool admin can call functions marked by this modifier.
                       */
                      modifier onlyPoolAdmin() {
                        IACLManager aclManager = IACLManager(_addressesProvider.getACLManager());
                        require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);
                        _;
                      }
                      /**
                       * @dev Only pool can call functions marked by this modifier.
                       */
                      modifier onlyPool() {
                        require(_msgSender() == address(POOL), Errors.CALLER_MUST_BE_POOL);
                        _;
                      }
                      /**
                       * @dev UserState - additionalData is a flexible field.
                       * ATokens and VariableDebtTokens use this field store the index of the
                       * user's last supply/withdrawal/borrow/repayment.
                       */
                      struct UserState {
                        uint128 balance;
                        uint128 additionalData;
                      }
                      // Map of users address and their state data (userAddress => userStateData)
                      mapping(address => UserState) internal _userState;
                      // Map of allowances (delegator => delegatee => allowanceAmount)
                      mapping(address => mapping(address => uint256)) private _allowances;
                      uint256 internal _totalSupply;
                      string private _name;
                      string private _symbol;
                      uint8 private _decimals;
                      IAaveIncentivesController internal _incentivesController;
                      IPoolAddressesProvider internal immutable _addressesProvider;
                      IPool public immutable POOL;
                      /**
                       * @dev Constructor.
                       * @param pool The reference to the main Pool contract
                       * @param name_ The name of the token
                       * @param symbol_ The symbol of the token
                       * @param decimals_ The number of decimals of the token
                       */
                      constructor(IPool pool, string memory name_, string memory symbol_, uint8 decimals_) {
                        _addressesProvider = pool.ADDRESSES_PROVIDER();
                        _name = name_;
                        _symbol = symbol_;
                        _decimals = decimals_;
                        POOL = pool;
                      }
                      /// @inheritdoc IERC20Detailed
                      function name() public view override returns (string memory) {
                        return _name;
                      }
                      /// @inheritdoc IERC20Detailed
                      function symbol() external view override returns (string memory) {
                        return _symbol;
                      }
                      /// @inheritdoc IERC20Detailed
                      function decimals() external view override returns (uint8) {
                        return _decimals;
                      }
                      /// @inheritdoc IERC20
                      function totalSupply() public view virtual override returns (uint256) {
                        return _totalSupply;
                      }
                      /// @inheritdoc IERC20
                      function balanceOf(address account) public view virtual override returns (uint256) {
                        return _userState[account].balance;
                      }
                      /**
                       * @notice Returns the address of the Incentives Controller contract
                       * @return The address of the Incentives Controller
                       */
                      function getIncentivesController() external view virtual returns (IAaveIncentivesController) {
                        return _incentivesController;
                      }
                      /**
                       * @notice Sets a new Incentives Controller
                       * @param controller the new Incentives controller
                       */
                      function setIncentivesController(IAaveIncentivesController controller) external onlyPoolAdmin {
                        _incentivesController = controller;
                      }
                      /// @inheritdoc IERC20
                      function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
                        uint128 castAmount = amount.toUint128();
                        _transfer(_msgSender(), recipient, castAmount);
                        return true;
                      }
                      /// @inheritdoc IERC20
                      function allowance(
                        address owner,
                        address spender
                      ) external view virtual override returns (uint256) {
                        return _allowances[owner][spender];
                      }
                      /// @inheritdoc IERC20
                      function approve(address spender, uint256 amount) external virtual override returns (bool) {
                        _approve(_msgSender(), spender, amount);
                        return true;
                      }
                      /// @inheritdoc IERC20
                      function transferFrom(
                        address sender,
                        address recipient,
                        uint256 amount
                      ) external virtual override returns (bool) {
                        uint128 castAmount = amount.toUint128();
                        _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - castAmount);
                        _transfer(sender, recipient, castAmount);
                        return true;
                      }
                      /**
                       * @notice 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) external virtual returns (bool) {
                        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                        return true;
                      }
                      /**
                       * @notice 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
                      ) external virtual returns (bool) {
                        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
                        return true;
                      }
                      /**
                       * @notice Transfers tokens between two users and apply incentives if defined.
                       * @param sender The source address
                       * @param recipient The destination address
                       * @param amount The amount getting transferred
                       */
                      function _transfer(address sender, address recipient, uint128 amount) internal virtual {
                        uint128 oldSenderBalance = _userState[sender].balance;
                        _userState[sender].balance = oldSenderBalance - amount;
                        uint128 oldRecipientBalance = _userState[recipient].balance;
                        _userState[recipient].balance = oldRecipientBalance + amount;
                        IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                        if (address(incentivesControllerLocal) != address(0)) {
                          uint256 currentTotalSupply = _totalSupply;
                          incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);
                          if (sender != recipient) {
                            incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                          }
                        }
                      }
                      /**
                       * @notice Approve `spender` to use `amount` of `owner`s balance
                       * @param owner The address owning the tokens
                       * @param spender The address approved for spending
                       * @param amount The amount of tokens to approve spending of
                       */
                      function _approve(address owner, address spender, uint256 amount) internal virtual {
                        _allowances[owner][spender] = amount;
                        emit Approval(owner, spender, amount);
                      }
                      /**
                       * @notice Update the name of the token
                       * @param newName The new name for the token
                       */
                      function _setName(string memory newName) internal {
                        _name = newName;
                      }
                      /**
                       * @notice Update the symbol for the token
                       * @param newSymbol The new symbol for the token
                       */
                      function _setSymbol(string memory newSymbol) internal {
                        _symbol = newSymbol;
                      }
                      /**
                       * @notice Update the number of decimals for the token
                       * @param newDecimals The new number of decimals for the token
                       */
                      function _setDecimals(uint8 newDecimals) internal {
                        _decimals = newDecimals;
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                    import {IPool} from './IPool.sol';
                    /**
                     * @title IInitializableDebtToken
                     * @author Aave
                     * @notice Interface for the initialize function common between debt tokens
                     */
                    interface IInitializableDebtToken {
                      /**
                       * @dev Emitted when a debt token is initialized
                       * @param underlyingAsset The address of the underlying asset
                       * @param pool The address of the associated 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
                      );
                      /**
                       * @notice Initializes the debt token.
                       * @param pool The pool contract that is initializing this contract
                       * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @param incentivesController The smart contract managing potential incentives distribution
                       * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's
                       * @param debtTokenName The name of the token
                       * @param debtTokenSymbol The symbol of the token
                       * @param params A set of encoded parameters for additional initialization
                       */
                      function initialize(
                        IPool pool,
                        address underlyingAsset,
                        IAaveIncentivesController incentivesController,
                        uint8 debtTokenDecimals,
                        string memory debtTokenName,
                        string memory debtTokenSymbol,
                        bytes calldata params
                      ) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IAaveIncentivesController
                     * @author Aave
                     * @notice Defines the basic interface for an Aave Incentives Controller.
                     * @dev It only contains one single function, needed as a hook on aToken and debtToken transfers.
                     */
                    interface IAaveIncentivesController {
                      /**
                       * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
                       * @dev The units of `totalSupply` and `userBalance` should be the same.
                       * @param user The address of the user whose asset balance has changed
                       * @param totalSupply The total supply of the asset prior to user balance change
                       * @param userBalance The previous user balance prior to balance change
                       */
                      function handleAction(address user, uint256 totalSupply, uint256 userBalance) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.10;
                    /*
                     * @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 payable(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: MIT
                    pragma solidity ^0.8.10;
                    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 5 of 12: SupplyLogic
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {Errors} from '../helpers/Errors.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    /**
                     * @title SupplyLogic library
                     * @author Aave
                     * @notice Implements the base logic for supply/withdraw
                     */
                    library SupplyLogic {
                      using ReserveLogic for DataTypes.ReserveCache;
                      using ReserveLogic for DataTypes.ReserveData;
                      using GPv2SafeERC20 for IERC20;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      // See `IPool` for descriptions
                      event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                      event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                      event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                      event Supply(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @notice Implements the supply feature. Through `supply()`, users supply assets to the Aave protocol.
                       * @dev Emits the `Supply()` event.
                       * @dev In the first supply action, `ReserveUsedAsCollateralEnabled()` is emitted, if the asset can be enabled as
                       * collateral.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the supply function
                       */
                      function executeSupply(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ExecuteSupplyParams memory params
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[params.asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        reserve.updateState(reserveCache);
                        ValidationLogic.validateSupply(reserveCache, reserve, params.amount, params.onBehalfOf);
                        reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, params.amount, 0);
                        IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, params.amount);
                        bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
                          msg.sender,
                          params.onBehalfOf,
                          params.amount,
                          reserveCache.nextLiquidityIndex
                        );
                        if (isFirstSupply) {
                          if (
                            ValidationLogic.validateAutomaticUseAsCollateral(
                              reservesData,
                              reservesList,
                              userConfig,
                              reserveCache.reserveConfiguration,
                              reserveCache.aTokenAddress
                            )
                          ) {
                            userConfig.setUsingAsCollateral(reserve.id, true);
                            emit ReserveUsedAsCollateralEnabled(params.asset, params.onBehalfOf);
                          }
                        }
                        emit Supply(params.asset, msg.sender, params.onBehalfOf, params.amount, params.referralCode);
                      }
                      /**
                       * @notice Implements the withdraw feature. Through `withdraw()`, users redeem their aTokens for the underlying asset
                       * previously supplied in the Aave protocol.
                       * @dev Emits the `Withdraw()` event.
                       * @dev If the user withdraws everything, `ReserveUsedAsCollateralDisabled()` is emitted.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the withdraw function
                       * @return The actual amount withdrawn
                       */
                      function executeWithdraw(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ExecuteWithdrawParams memory params
                      ) external returns (uint256) {
                        DataTypes.ReserveData storage reserve = reservesData[params.asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        require(params.to != reserveCache.aTokenAddress, Errors.WITHDRAW_TO_ATOKEN);
                        reserve.updateState(reserveCache);
                        uint256 userBalance = IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul(
                          reserveCache.nextLiquidityIndex
                        );
                        uint256 amountToWithdraw = params.amount;
                        if (params.amount == type(uint256).max) {
                          amountToWithdraw = userBalance;
                        }
                        ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance);
                        reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, 0, amountToWithdraw);
                        bool isCollateral = userConfig.isUsingAsCollateral(reserve.id);
                        if (isCollateral && amountToWithdraw == userBalance) {
                          userConfig.setUsingAsCollateral(reserve.id, false);
                          emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
                        }
                        IAToken(reserveCache.aTokenAddress).burn(
                          msg.sender,
                          params.to,
                          amountToWithdraw,
                          reserveCache.nextLiquidityIndex
                        );
                        if (isCollateral && userConfig.isBorrowingAny()) {
                          ValidationLogic.validateHFAndLtv(
                            reservesData,
                            reservesList,
                            eModeCategories,
                            userConfig,
                            params.asset,
                            msg.sender,
                            params.reservesCount,
                            params.oracle,
                            params.userEModeCategory
                          );
                        }
                        emit Withdraw(params.asset, msg.sender, params.to, amountToWithdraw);
                        return amountToWithdraw;
                      }
                      /**
                       * @notice Validates a transfer of aTokens. The sender is subjected to health factor validation to avoid
                       * collateralization constraints violation.
                       * @dev Emits the `ReserveUsedAsCollateralEnabled()` event for the `to` account, if the asset is being activated as
                       * collateral.
                       * @dev In case the `from` user transfers everything, `ReserveUsedAsCollateralDisabled()` is emitted for `from`.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the finalizeTransfer function
                       */
                      function executeFinalizeTransfer(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
                        DataTypes.FinalizeTransferParams memory params
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[params.asset];
                        ValidationLogic.validateTransfer(reserve);
                        uint256 reserveId = reserve.id;
                        uint256 scaledAmount = params.amount.rayDiv(reserve.getNormalizedIncome());
                        if (params.from != params.to && scaledAmount != 0) {
                          DataTypes.UserConfigurationMap storage fromConfig = usersConfig[params.from];
                          if (fromConfig.isUsingAsCollateral(reserveId)) {
                            if (fromConfig.isBorrowingAny()) {
                              ValidationLogic.validateHFAndLtv(
                                reservesData,
                                reservesList,
                                eModeCategories,
                                usersConfig[params.from],
                                params.asset,
                                params.from,
                                params.reservesCount,
                                params.oracle,
                                params.fromEModeCategory
                              );
                            }
                            if (params.balanceFromBefore == params.amount) {
                              fromConfig.setUsingAsCollateral(reserveId, false);
                              emit ReserveUsedAsCollateralDisabled(params.asset, params.from);
                            }
                          }
                          if (params.balanceToBefore == 0) {
                            DataTypes.UserConfigurationMap storage toConfig = usersConfig[params.to];
                            if (
                              ValidationLogic.validateAutomaticUseAsCollateral(
                                reservesData,
                                reservesList,
                                toConfig,
                                reserve.configuration,
                                reserve.aTokenAddress
                              )
                            ) {
                              toConfig.setUsingAsCollateral(reserveId, true);
                              emit ReserveUsedAsCollateralEnabled(params.asset, params.to);
                            }
                          }
                        }
                      }
                      /**
                       * @notice Executes the 'set as collateral' feature. A user can choose to activate or deactivate an asset as
                       * collateral at any point in time. Deactivating an asset as collateral is subjected to the usual health factor
                       * checks to ensure collateralization.
                       * @dev Emits the `ReserveUsedAsCollateralEnabled()` event if the asset can be activated as collateral.
                       * @dev In case the asset is being deactivated as collateral, `ReserveUsedAsCollateralDisabled()` is emitted.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The users configuration mapping that track the supplied/borrowed assets
                       * @param asset The address of the asset being configured as collateral
                       * @param useAsCollateral True if the user wants to set the asset as collateral, false otherwise
                       * @param reservesCount The number of initialized reserves
                       * @param priceOracle The address of the price oracle
                       * @param userEModeCategory The eMode category chosen by the user
                       */
                      function executeUseReserveAsCollateral(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap storage userConfig,
                        address asset,
                        bool useAsCollateral,
                        uint256 reservesCount,
                        address priceOracle,
                        uint8 userEModeCategory
                      ) external {
                        DataTypes.ReserveData storage reserve = reservesData[asset];
                        DataTypes.ReserveCache memory reserveCache = reserve.cache();
                        uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender);
                        ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance);
                        if (useAsCollateral == userConfig.isUsingAsCollateral(reserve.id)) return;
                        if (useAsCollateral) {
                          require(
                            ValidationLogic.validateUseAsCollateral(
                              reservesData,
                              reservesList,
                              userConfig,
                              reserveCache.reserveConfiguration
                            ),
                            Errors.USER_IN_ISOLATION_MODE_OR_LTV_ZERO
                          );
                          userConfig.setUsingAsCollateral(reserve.id, true);
                          emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
                        } else {
                          userConfig.setUsingAsCollateral(reserve.id, false);
                          ValidationLogic.validateHFAndLtv(
                            reservesData,
                            reservesList,
                            eModeCategories,
                            userConfig,
                            asset,
                            msg.sender,
                            reservesCount,
                            priceOracle,
                            userEModeCategory
                          );
                          emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @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: LGPL-3.0-or-later
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../openzeppelin/contracts/IERC20.sol';
                    /// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
                    /// @author Gnosis Developers
                    /// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract.
                    library GPv2SafeERC20 {
                      /// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
                      /// also when the token returns `false`.
                      function safeTransfer(IERC20 token, address to, uint256 value) internal {
                        bytes4 selector_ = token.transfer.selector;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          let freeMemoryPointer := mload(0x40)
                          mstore(freeMemoryPointer, selector_)
                          mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 36), value)
                          if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
                            returndatacopy(0, 0, returndatasize())
                            revert(0, returndatasize())
                          }
                        }
                        require(getLastTransferResult(token), 'GPv2: failed transfer');
                      }
                      /// @dev Wrapper around a call to the ERC20 function `transferFrom` that
                      /// reverts also when the token returns `false`.
                      function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                        bytes4 selector_ = token.transferFrom.selector;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          let freeMemoryPointer := mload(0x40)
                          mstore(freeMemoryPointer, selector_)
                          mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 68), value)
                          if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
                            returndatacopy(0, 0, returndatasize())
                            revert(0, returndatasize())
                          }
                        }
                        require(getLastTransferResult(token), 'GPv2: failed transferFrom');
                      }
                      /// @dev Verifies that the last return was a successful `transfer*` call.
                      /// This is done by checking that the return data is either empty, or
                      /// is a valid ABI encoded boolean.
                      function getLastTransferResult(IERC20 token) private view returns (bool success) {
                        // NOTE: Inspecting previous return data requires assembly. Note that
                        // we write the return data to memory 0 in the case where the return
                        // data size is 32, this is OK since the first 64 bytes of memory are
                        // reserved by Solidy as a scratch space that can be used within
                        // assembly blocks.
                        // <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          /// @dev Revert with an ABI encoded Solidity error with a message
                          /// that fits into 32-bytes.
                          ///
                          /// An ABI encoded Solidity error has the following memory layout:
                          ///
                          /// ------------+----------------------------------
                          ///  byte range | value
                          /// ------------+----------------------------------
                          ///  0x00..0x04 |        selector("Error(string)")
                          ///  0x04..0x24 |      string offset (always 0x20)
                          ///  0x24..0x44 |                    string length
                          ///  0x44..0x64 | string value, padded to 32-bytes
                          function revertWithMessage(length, message) {
                            mstore(0x00, '\\x08\\xc3\\x79\\xa0')
                            mstore(0x04, 0x20)
                            mstore(0x24, length)
                            mstore(0x44, message)
                            revert(0x00, 0x64)
                          }
                          switch returndatasize()
                          // Non-standard ERC20 transfer without return.
                          case 0 {
                            // NOTE: When the return data size is 0, verify that there
                            // is code at the address. This is done in order to maintain
                            // compatibility with Solidity calling conventions.
                            // <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
                            if iszero(extcodesize(token)) {
                              revertWithMessage(20, 'GPv2: not a contract')
                            }
                            success := 1
                          }
                          // Standard ERC20 transfer returning boolean success value.
                          case 32 {
                            returndatacopy(0, 0, returndatasize())
                            // NOTE: For ABI encoding v1, any non-zero value is accepted
                            // as `true` for a boolean. In order to stay compatible with
                            // OpenZeppelin's `SafeERC20` library which is known to work
                            // with the existing ERC20 implementation we care about,
                            // make sure we return success for any non-zero return value
                            // from the `transfer*` call.
                            success := iszero(iszero(mload(0)))
                          }
                          default {
                            revertWithMessage(31, 'GPv2: malformed transfer result')
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                    import {IInitializableAToken} from './IInitializableAToken.sol';
                    /**
                     * @title IAToken
                     * @author Aave
                     * @notice Defines the basic interface for an AToken.
                     */
                    interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
                      /**
                       * @dev Emitted during the transfer action
                       * @param from The user whose tokens are being transferred
                       * @param to The recipient
                       * @param value The scaled amount being transferred
                       * @param index The next liquidity index of the reserve
                       */
                      event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index);
                      /**
                       * @notice Mints `amount` aTokens to `user`
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the minted aTokens
                       * @param amount The amount of tokens getting minted
                       * @param index The next liquidity index of the reserve
                       * @return `true` if the the previous balance of the user was 0
                       */
                      function mint(
                        address caller,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) external returns (bool);
                      /**
                       * @notice Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                       * @dev In some instances, the mint event could be emitted from a burn transaction
                       * if the amount to burn is less than the interest that the user accrued
                       * @param from The address from which the aTokens will be burned
                       * @param receiverOfUnderlying The address that will receive the underlying
                       * @param amount The amount being burned
                       * @param index The next liquidity index of the reserve
                       */
                      function burn(address from, address receiverOfUnderlying, uint256 amount, uint256 index) external;
                      /**
                       * @notice Mints aTokens to the reserve treasury
                       * @param amount The amount of tokens getting minted
                       * @param index The next liquidity index of the reserve
                       */
                      function mintToTreasury(uint256 amount, uint256 index) external;
                      /**
                       * @notice 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;
                      /**
                       * @notice Transfers the underlying asset to `target`.
                       * @dev Used by the Pool to transfer assets in borrow(), withdraw() and flashLoan()
                       * @param target The recipient of the underlying
                       * @param amount The amount getting transferred
                       */
                      function transferUnderlyingTo(address target, uint256 amount) external;
                      /**
                       * @notice Handles the underlying received by the aToken after the transfer has been completed.
                       * @dev The default implementation is empty as with standard ERC20 tokens, nothing needs to be done after the
                       * transfer is concluded. However in the future there may be aTokens that allow for example to stake the underlying
                       * to receive LM rewards. In that case, `handleRepayment()` would perform the staking of the underlying asset.
                       * @param user The user executing the repayment
                       * @param onBehalfOf The address of the user who will get his debt reduced/removed
                       * @param amount The amount getting repaid
                       */
                      function handleRepayment(address user, address onBehalfOf, uint256 amount) external;
                      /**
                       * @notice Allow passing a signed message to approve spending
                       * @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;
                      /**
                       * @notice Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @return The address of the underlying asset
                       */
                      function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                      /**
                       * @notice Returns the address of the Aave treasury, receiving the fees on this aToken.
                       * @return Address of the Aave treasury
                       */
                      function RESERVE_TREASURY_ADDRESS() external view returns (address);
                      /**
                       * @notice Get the domain separator for the token
                       * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                       * @return The domain separator of the token at current chain
                       */
                      function DOMAIN_SEPARATOR() external view returns (bytes32);
                      /**
                       * @notice Returns the nonce for owner.
                       * @param owner The address of the owner
                       * @return The nonce of the owner
                       */
                      function nonces(address owner) external view returns (uint256);
                      /**
                       * @notice Rescue and transfer tokens locked in this contract
                       * @param token The address of the token
                       * @param to The address of the recipient
                       * @param amount The amount of token to transfer
                       */
                      function rescueTokens(address token, address to, uint256 amount) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title Errors library
                     * @author Aave
                     * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                     */
                    library Errors {
                      string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                      string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                      string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                      string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                      string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                      string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                      string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                      string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                      string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                      string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                      string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                      string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                      string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                      string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                      string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                      string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                      string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                      string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                      string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                      string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                      string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                      string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                      string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                      string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                      string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                      string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                      string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                      string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                      string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                      string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                      string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                      string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                      string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                      string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                      string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                      string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                      string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                      string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                      string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                      string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                      string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                      string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                      string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                      string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                      string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                      string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                      string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                      string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                      string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                      string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                      string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                      string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                      string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                      string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                      string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                      string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                      string public constant USER_IN_ISOLATION_MODE_OR_LTV_ZERO = '62'; // 'User is in isolation mode or ltv is zero'
                      string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                      string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                      string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                      string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                      string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                      string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                      string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                      string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                      string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                      string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                      string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                      string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                      string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                      string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                      string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                      string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                      string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                      string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                      string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                      string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                      string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                      string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                      string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                      string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                      string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                      string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                      string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                      string public constant INVALID_MAX_RATE = '92'; // The expect maximum borrow rate is invalid
                      string public constant WITHDRAW_TO_ATOKEN = '93'; // Withdrawing to the aToken is not allowed
                      string public constant SUPPLY_TO_ATOKEN = '94'; // Supplying to the aToken is not allowed
                      string public constant SLOPE_2_MUST_BE_GTE_SLOPE_1 = '95'; // Variable interest rate slope 2 can not be lower than slope 1
                      string public constant CALLER_NOT_RISK_OR_POOL_OR_EMERGENCY_ADMIN = '96'; // 'The caller of the function is not a risk, pool or emergency admin'
                      string public constant LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED = '97'; // 'Liquidation grace sentinel validation failed'
                      string public constant INVALID_GRACE_PERIOD = '98'; // Grace period above a valid range
                      string public constant INVALID_FREEZE_STATE = '99'; // Reserve is already in the passed freeze state
                      string public constant NOT_BORROWABLE_IN_EMODE = '100'; // Asset not borrowable in eMode
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {Errors} from '../helpers/Errors.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveConfiguration} from './ReserveConfiguration.sol';
                    /**
                     * @title UserConfiguration library
                     * @author Aave
                     * @notice Implements the bitmap logic to handle the user configuration
                     */
                    library UserConfiguration {
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      uint256 internal constant BORROWING_MASK =
                        0x5555555555555555555555555555555555555555555555555555555555555555;
                      uint256 internal constant COLLATERAL_MASK =
                        0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;
                      /**
                       * @notice 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 {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          uint256 bit = 1 << (reserveIndex << 1);
                          if (borrowing) {
                            self.data |= bit;
                          } else {
                            self.data &= ~bit;
                          }
                        }
                      }
                      /**
                       * @notice 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 using the reserve as collateral, false otherwise
                       */
                      function setUsingAsCollateral(
                        DataTypes.UserConfigurationMap storage self,
                        uint256 reserveIndex,
                        bool usingAsCollateral
                      ) internal {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          uint256 bit = 1 << ((reserveIndex << 1) + 1);
                          if (usingAsCollateral) {
                            self.data |= bit;
                          } else {
                            self.data &= ~bit;
                          }
                        }
                      }
                      /**
                       * @notice Returns 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) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          return (self.data >> (reserveIndex << 1)) & 3 != 0;
                        }
                      }
                      /**
                       * @notice Validate 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) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          return (self.data >> (reserveIndex << 1)) & 1 != 0;
                        }
                      }
                      /**
                       * @notice Validate 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) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0;
                        }
                      }
                      /**
                       * @notice Checks if a user has been supplying only one reserve as collateral
                       * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0
                       * @param self The configuration object
                       * @return True if the user has been supplying as collateral one reserve, false otherwise
                       */
                      function isUsingAsCollateralOne(
                        DataTypes.UserConfigurationMap memory self
                      ) internal pure returns (bool) {
                        uint256 collateralData = self.data & COLLATERAL_MASK;
                        return collateralData != 0 && (collateralData & (collateralData - 1) == 0);
                      }
                      /**
                       * @notice Checks if a user has been supplying any reserve as collateral
                       * @param self The configuration object
                       * @return True if the user has been supplying as collateral any reserve, false otherwise
                       */
                      function isUsingAsCollateralAny(
                        DataTypes.UserConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return self.data & COLLATERAL_MASK != 0;
                      }
                      /**
                       * @notice Checks if a user has been borrowing only one asset
                       * @dev this uses a simple trick - if a number is a power of two (only one bit set) then n & (n - 1) == 0
                       * @param self The configuration object
                       * @return True if the user has been supplying as collateral one reserve, false otherwise
                       */
                      function isBorrowingOne(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                        uint256 borrowingData = self.data & BORROWING_MASK;
                        return borrowingData != 0 && (borrowingData & (borrowingData - 1) == 0);
                      }
                      /**
                       * @notice Checks 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;
                      }
                      /**
                       * @notice Checks if a user has not been using any reserve for borrowing or supply
                       * @param self The configuration object
                       * @return True if the user has not been borrowing or supplying any reserve, false otherwise
                       */
                      function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                        return self.data == 0;
                      }
                      /**
                       * @notice Returns the Isolation Mode state of the user
                       * @param self The configuration object
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @return True if the user is in isolation mode, false otherwise
                       * @return The address of the only asset used as collateral
                       * @return The debt ceiling of the reserve
                       */
                      function getIsolationModeState(
                        DataTypes.UserConfigurationMap memory self,
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList
                      ) internal view returns (bool, address, uint256) {
                        if (isUsingAsCollateralOne(self)) {
                          uint256 assetId = _getFirstAssetIdByMask(self, COLLATERAL_MASK);
                          address assetAddress = reservesList[assetId];
                          uint256 ceiling = reservesData[assetAddress].configuration.getDebtCeiling();
                          if (ceiling != 0) {
                            return (true, assetAddress, ceiling);
                          }
                        }
                        return (false, address(0), 0);
                      }
                      /**
                       * @notice Returns the siloed borrowing state for the user
                       * @param self The configuration object
                       * @param reservesData The data of all the reserves
                       * @param reservesList The reserve list
                       * @return True if the user has borrowed a siloed asset, false otherwise
                       * @return The address of the only borrowed asset
                       */
                      function getSiloedBorrowingState(
                        DataTypes.UserConfigurationMap memory self,
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList
                      ) internal view returns (bool, address) {
                        if (isBorrowingOne(self)) {
                          uint256 assetId = _getFirstAssetIdByMask(self, BORROWING_MASK);
                          address assetAddress = reservesList[assetId];
                          if (reservesData[assetAddress].configuration.getSiloedBorrowing()) {
                            return (true, assetAddress);
                          }
                        }
                        return (false, address(0));
                      }
                      /**
                       * @notice Returns the address of the first asset flagged in the bitmap given the corresponding bitmask
                       * @param self The configuration object
                       * @return The index of the first asset flagged in the bitmap once the corresponding mask is applied
                       */
                      function _getFirstAssetIdByMask(
                        DataTypes.UserConfigurationMap memory self,
                        uint256 mask
                      ) internal pure returns (uint256) {
                        unchecked {
                          uint256 bitmapData = self.data & mask;
                          uint256 firstAssetPosition = bitmapData & ~(bitmapData - 1);
                          uint256 id;
                          while ((firstAssetPosition >>= 2) != 0) {
                            id += 1;
                          }
                          return id;
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    library DataTypes {
                      /**
                       * This exists specifically to maintain the `getReserveData()` interface, since the new, internal
                       * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`.
                       */
                      struct ReserveDataLegacy {
                        //stores the reserve configuration
                        ReserveConfigurationMap configuration;
                        //the liquidity index. Expressed in ray
                        uint128 liquidityIndex;
                        //the current supply rate. Expressed in ray
                        uint128 currentLiquidityRate;
                        //variable borrow index. Expressed in ray
                        uint128 variableBorrowIndex;
                        //the current variable borrow rate. Expressed in ray
                        uint128 currentVariableBorrowRate;
                        // DEPRECATED on v3.2.0
                        uint128 currentStableBorrowRate;
                        //timestamp of last update
                        uint40 lastUpdateTimestamp;
                        //the id of the reserve. Represents the position in the list of the active reserves
                        uint16 id;
                        //aToken address
                        address aTokenAddress;
                        // DEPRECATED on v3.2.0
                        address stableDebtTokenAddress;
                        //variableDebtToken address
                        address variableDebtTokenAddress;
                        //address of the interest rate strategy
                        address interestRateStrategyAddress;
                        //the current treasury balance, scaled
                        uint128 accruedToTreasury;
                        //the outstanding unbacked aTokens minted through the bridging feature
                        uint128 unbacked;
                        //the outstanding debt borrowed against this asset in isolation mode
                        uint128 isolationModeTotalDebt;
                      }
                      struct ReserveData {
                        //stores the reserve configuration
                        ReserveConfigurationMap configuration;
                        //the liquidity index. Expressed in ray
                        uint128 liquidityIndex;
                        //the current supply rate. Expressed in ray
                        uint128 currentLiquidityRate;
                        //variable borrow index. Expressed in ray
                        uint128 variableBorrowIndex;
                        //the current variable borrow rate. Expressed in ray
                        uint128 currentVariableBorrowRate;
                        // DEPRECATED on v3.2.0
                        uint128 __deprecatedStableBorrowRate;
                        //timestamp of last update
                        uint40 lastUpdateTimestamp;
                        //the id of the reserve. Represents the position in the list of the active reserves
                        uint16 id;
                        //timestamp until when liquidations are not allowed on the reserve, if set to past liquidations will be allowed
                        uint40 liquidationGracePeriodUntil;
                        //aToken address
                        address aTokenAddress;
                        // DEPRECATED on v3.2.0
                        address __deprecatedStableDebtTokenAddress;
                        //variableDebtToken address
                        address variableDebtTokenAddress;
                        //address of the interest rate strategy
                        address interestRateStrategyAddress;
                        //the current treasury balance, scaled
                        uint128 accruedToTreasury;
                        //the outstanding unbacked aTokens minted through the bridging feature
                        uint128 unbacked;
                        //the outstanding debt borrowed against this asset in isolation mode
                        uint128 isolationModeTotalDebt;
                        //the amount of underlying accounted for by the protocol
                        uint128 virtualUnderlyingBalance;
                      }
                      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: DEPRECATED: stable rate borrowing enabled
                        //bit 60: asset is paused
                        //bit 61: borrowing in isolation mode is enabled
                        //bit 62: siloed borrowing enabled
                        //bit 63: flashloaning enabled
                        //bit 64-79: reserve factor
                        //bit 80-115: borrow cap in whole tokens, borrowCap == 0 => no cap
                        //bit 116-151: supply cap in whole tokens, supplyCap == 0 => no cap
                        //bit 152-167: liquidation protocol fee
                        //bit 168-175: DEPRECATED: eMode category
                        //bit 176-211: unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                        //bit 212-251: debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                        //bit 252: virtual accounting is enabled for the reserve
                        //bit 253-255 unused
                        uint256 data;
                      }
                      struct UserConfigurationMap {
                        /**
                         * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                         * The first bit indicates if an asset is used as collateral by the user, the second whether an
                         * asset is borrowed by the user.
                         */
                        uint256 data;
                      }
                      // DEPRECATED: kept for backwards compatibility, might be removed in a future version
                      struct EModeCategoryLegacy {
                        // each eMode category has a custom ltv and liquidation threshold
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        // DEPRECATED
                        address priceSource;
                        string label;
                      }
                      struct CollateralConfig {
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                      }
                      struct EModeCategoryBaseConfiguration {
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        string label;
                      }
                      struct EModeCategory {
                        // each eMode category has a custom ltv and liquidation threshold
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        uint128 collateralBitmap;
                        string label;
                        uint128 borrowableBitmap;
                      }
                      enum InterestRateMode {
                        NONE,
                        __DEPRECATED,
                        VARIABLE
                      }
                      struct ReserveCache {
                        uint256 currScaledVariableDebt;
                        uint256 nextScaledVariableDebt;
                        uint256 currLiquidityIndex;
                        uint256 nextLiquidityIndex;
                        uint256 currVariableBorrowIndex;
                        uint256 nextVariableBorrowIndex;
                        uint256 currLiquidityRate;
                        uint256 currVariableBorrowRate;
                        uint256 reserveFactor;
                        ReserveConfigurationMap reserveConfiguration;
                        address aTokenAddress;
                        address variableDebtTokenAddress;
                        uint40 reserveLastUpdateTimestamp;
                      }
                      struct ExecuteLiquidationCallParams {
                        uint256 reservesCount;
                        uint256 debtToCover;
                        address collateralAsset;
                        address debtAsset;
                        address user;
                        bool receiveAToken;
                        address priceOracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteSupplyParams {
                        address asset;
                        uint256 amount;
                        address onBehalfOf;
                        uint16 referralCode;
                      }
                      struct ExecuteBorrowParams {
                        address asset;
                        address user;
                        address onBehalfOf;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint16 referralCode;
                        bool releaseUnderlying;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteRepayParams {
                        address asset;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        address onBehalfOf;
                        bool useATokens;
                      }
                      struct ExecuteWithdrawParams {
                        address asset;
                        uint256 amount;
                        address to;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ExecuteSetUserEModeParams {
                        uint256 reservesCount;
                        address oracle;
                        uint8 categoryId;
                      }
                      struct FinalizeTransferParams {
                        address asset;
                        address from;
                        address to;
                        uint256 amount;
                        uint256 balanceFromBefore;
                        uint256 balanceToBefore;
                        uint256 reservesCount;
                        address oracle;
                        uint8 fromEModeCategory;
                      }
                      struct FlashloanParams {
                        address receiverAddress;
                        address[] assets;
                        uint256[] amounts;
                        uint256[] interestRateModes;
                        address onBehalfOf;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                        uint256 reservesCount;
                        address addressesProvider;
                        address pool;
                        uint8 userEModeCategory;
                        bool isAuthorizedFlashBorrower;
                      }
                      struct FlashloanSimpleParams {
                        address receiverAddress;
                        address asset;
                        uint256 amount;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                      }
                      struct FlashLoanRepaymentParams {
                        uint256 amount;
                        uint256 totalPremium;
                        uint256 flashLoanPremiumToProtocol;
                        address asset;
                        address receiverAddress;
                        uint16 referralCode;
                      }
                      struct CalculateUserAccountDataParams {
                        UserConfigurationMap userConfig;
                        uint256 reservesCount;
                        address user;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ValidateBorrowParams {
                        ReserveCache reserveCache;
                        UserConfigurationMap userConfig;
                        address asset;
                        address userAddress;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                        bool isolationModeActive;
                        address isolationModeCollateralAddress;
                        uint256 isolationModeDebtCeiling;
                      }
                      struct ValidateLiquidationCallParams {
                        ReserveCache debtReserveCache;
                        uint256 totalDebt;
                        uint256 healthFactor;
                        address priceOracleSentinel;
                      }
                      struct CalculateInterestRatesParams {
                        uint256 unbacked;
                        uint256 liquidityAdded;
                        uint256 liquidityTaken;
                        uint256 totalDebt;
                        uint256 reserveFactor;
                        address reserve;
                        bool usingVirtualBalance;
                        uint256 virtualUnderlyingBalance;
                      }
                      struct InitReserveParams {
                        address asset;
                        address aTokenAddress;
                        address variableDebtAddress;
                        address interestRateStrategyAddress;
                        uint16 reservesCount;
                        uint16 maxNumberReserves;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @title WadRayMath library
                     * @author Aave
                     * @notice Provides functions to perform calculations with Wad and Ray units
                     * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                     * with 27 digits of precision)
                     * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                     */
                    library WadRayMath {
                      // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                      uint256 internal constant WAD = 1e18;
                      uint256 internal constant HALF_WAD = 0.5e18;
                      uint256 internal constant RAY = 1e27;
                      uint256 internal constant HALF_RAY = 0.5e27;
                      uint256 internal constant WAD_RAY_RATIO = 1e9;
                      /**
                       * @dev Multiplies two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a*b, in wad
                       */
                      function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_WAD), WAD)
                        }
                      }
                      /**
                       * @dev Divides two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a/b, in wad
                       */
                      function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, WAD), div(b, 2)), b)
                        }
                      }
                      /**
                       * @notice Multiplies two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raymul b
                       */
                      function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_RAY), RAY)
                        }
                      }
                      /**
                       * @notice Divides two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raydiv b
                       */
                      function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, RAY), div(b, 2)), b)
                        }
                      }
                      /**
                       * @dev Casts ray down to wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @return b = a converted to wad, rounded half up to the nearest wad
                       */
                      function rayToWad(uint256 a) internal pure returns (uint256 b) {
                        assembly {
                          b := div(a, WAD_RAY_RATIO)
                          let remainder := mod(a, WAD_RAY_RATIO)
                          if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                            b := add(b, 1)
                          }
                        }
                      }
                      /**
                       * @dev Converts wad up to ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @return b = a converted in ray
                       */
                      function wadToRay(uint256 a) internal pure returns (uint256 b) {
                        // to avoid overflow, b/WAD_RAY_RATIO == a
                        assembly {
                          b := mul(a, WAD_RAY_RATIO)
                          if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                            revert(0, 0)
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @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. If a value is >=.5, will be rounded up, otherwise rounded down.
                     */
                    library PercentageMath {
                      // Maximum percentage factor (100.00%)
                      uint256 internal constant PERCENTAGE_FACTOR = 1e4;
                      // Half percentage factor (50.00%)
                      uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4;
                      /**
                       * @notice Executes a percentage multiplication
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param value The value of which the percentage needs to be calculated
                       * @param percentage The percentage of the value to be calculated
                       * @return result value percentmul percentage
                       */
                      function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                        // to avoid overflow, value <= (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentage
                        assembly {
                          if iszero(
                            or(
                              iszero(percentage),
                              iszero(gt(value, div(sub(not(0), HALF_PERCENTAGE_FACTOR), percentage)))
                            )
                          ) {
                            revert(0, 0)
                          }
                          result := div(add(mul(value, percentage), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
                        }
                      }
                      /**
                       * @notice Executes a percentage division
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param value The value of which the percentage needs to be calculated
                       * @param percentage The percentage of the value to be calculated
                       * @return result value percentdiv percentage
                       */
                      function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                        // to avoid overflow, value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR
                        assembly {
                          if or(
                            iszero(percentage),
                            iszero(iszero(gt(value, div(sub(not(0), div(percentage, 2)), PERCENTAGE_FACTOR))))
                          ) {
                            revert(0, 0)
                          }
                          result := div(add(mul(value, PERCENTAGE_FACTOR), div(percentage, 2)), percentage)
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {Address} from '../../../dependencies/openzeppelin/contracts/Address.sol';
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
                    import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                    import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                    import {IAToken} from '../../../interfaces/IAToken.sol';
                    import {IPriceOracleSentinel} from '../../../interfaces/IPriceOracleSentinel.sol';
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {IAccessControl} from '../../../dependencies/openzeppelin/contracts/IAccessControl.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
                    import {Errors} from '../helpers/Errors.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {GenericLogic} from './GenericLogic.sol';
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {IncentivizedERC20} from '../../tokenization/base/IncentivizedERC20.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 WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using SafeCast for uint256;
                      using GPv2SafeERC20 for IERC20;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using Address for address;
                      // Factor to apply to "only-variable-debt" liquidity rate to get threshold for rebalancing, expressed in bps
                      // A value of 0.9e4 results in 90%
                      uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 0.9e4;
                      // Minimum health factor allowed under any circumstance
                      // A value of 0.95e18 results in 0.95
                      uint256 public constant MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 0.95e18;
                      /**
                       * @dev Minimum health factor to consider a user position healthy
                       * A value of 1e18 results in 1
                       */
                      uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;
                      /**
                       * @dev Role identifier for the role allowed to supply isolated reserves as collateral
                       */
                      bytes32 public constant ISOLATED_COLLATERAL_SUPPLIER_ROLE =
                        keccak256('ISOLATED_COLLATERAL_SUPPLIER');
                      /**
                       * @notice Validates a supply action.
                       * @param reserveCache The cached data of the reserve
                       * @param amount The amount to be supplied
                       */
                      function validateSupply(
                        DataTypes.ReserveCache memory reserveCache,
                        DataTypes.ReserveData storage reserve,
                        uint256 amount,
                        address onBehalfOf
                      ) internal view {
                        require(amount != 0, Errors.INVALID_AMOUNT);
                        (bool isActive, bool isFrozen, , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                        require(isActive, Errors.RESERVE_INACTIVE);
                        require(!isPaused, Errors.RESERVE_PAUSED);
                        require(!isFrozen, Errors.RESERVE_FROZEN);
                        require(onBehalfOf != reserveCache.aTokenAddress, Errors.SUPPLY_TO_ATOKEN);
                        uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCap();
                        require(
                          supplyCap == 0 ||
                            ((IAToken(reserveCache.aTokenAddress).scaledTotalSupply() +
                              uint256(reserve.accruedToTreasury)).rayMul(reserveCache.nextLiquidityIndex) + amount) <=
                            supplyCap * (10 ** reserveCache.reserveConfiguration.getDecimals()),
                          Errors.SUPPLY_CAP_EXCEEDED
                        );
                      }
                      /**
                       * @notice Validates a withdraw action.
                       * @param reserveCache The cached data of the reserve
                       * @param amount The amount to be withdrawn
                       * @param userBalance The balance of the user
                       */
                      function validateWithdraw(
                        DataTypes.ReserveCache memory reserveCache,
                        uint256 amount,
                        uint256 userBalance
                      ) internal pure {
                        require(amount != 0, Errors.INVALID_AMOUNT);
                        require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);
                        (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                        require(isActive, Errors.RESERVE_INACTIVE);
                        require(!isPaused, Errors.RESERVE_PAUSED);
                      }
                      struct ValidateBorrowLocalVars {
                        uint256 currentLtv;
                        uint256 collateralNeededInBaseCurrency;
                        uint256 userCollateralInBaseCurrency;
                        uint256 userDebtInBaseCurrency;
                        uint256 availableLiquidity;
                        uint256 healthFactor;
                        uint256 totalDebt;
                        uint256 totalSupplyVariableDebt;
                        uint256 reserveDecimals;
                        uint256 borrowCap;
                        uint256 amountInBaseCurrency;
                        uint256 assetUnit;
                        address siloedBorrowingAddress;
                        bool isActive;
                        bool isFrozen;
                        bool isPaused;
                        bool borrowingEnabled;
                        bool siloedBorrowingEnabled;
                      }
                      /**
                       * @notice Validates a borrow action.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param params Additional params needed for the validation
                       */
                      function validateBorrow(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.ValidateBorrowParams memory params
                      ) internal view {
                        require(params.amount != 0, Errors.INVALID_AMOUNT);
                        ValidateBorrowLocalVars memory vars;
                        (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.isPaused) = params
                          .reserveCache
                          .reserveConfiguration
                          .getFlags();
                        require(vars.isActive, Errors.RESERVE_INACTIVE);
                        require(!vars.isPaused, Errors.RESERVE_PAUSED);
                        require(!vars.isFrozen, Errors.RESERVE_FROZEN);
                        require(vars.borrowingEnabled, Errors.BORROWING_NOT_ENABLED);
                        require(
                          !params.reserveCache.reserveConfiguration.getIsVirtualAccActive() ||
                            IERC20(params.reserveCache.aTokenAddress).totalSupply() >= params.amount,
                          Errors.INVALID_AMOUNT
                        );
                        require(
                          params.priceOracleSentinel == address(0) ||
                            IPriceOracleSentinel(params.priceOracleSentinel).isBorrowAllowed(),
                          Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                        );
                        //validate interest rate mode
                        require(
                          params.interestRateMode == DataTypes.InterestRateMode.VARIABLE,
                          Errors.INVALID_INTEREST_RATE_MODE_SELECTED
                        );
                        vars.reserveDecimals = params.reserveCache.reserveConfiguration.getDecimals();
                        vars.borrowCap = params.reserveCache.reserveConfiguration.getBorrowCap();
                        unchecked {
                          vars.assetUnit = 10 ** vars.reserveDecimals;
                        }
                        if (vars.borrowCap != 0) {
                          vars.totalSupplyVariableDebt = params.reserveCache.currScaledVariableDebt.rayMul(
                            params.reserveCache.nextVariableBorrowIndex
                          );
                          vars.totalDebt = vars.totalSupplyVariableDebt + params.amount;
                          unchecked {
                            require(vars.totalDebt <= vars.borrowCap * vars.assetUnit, Errors.BORROW_CAP_EXCEEDED);
                          }
                        }
                        if (params.isolationModeActive) {
                          // check that the asset being borrowed is borrowable in isolation mode AND
                          // the total exposure is no bigger than the collateral debt ceiling
                          require(
                            params.reserveCache.reserveConfiguration.getBorrowableInIsolation(),
                            Errors.ASSET_NOT_BORROWABLE_IN_ISOLATION
                          );
                          require(
                            reservesData[params.isolationModeCollateralAddress].isolationModeTotalDebt +
                              (params.amount /
                                10 ** (vars.reserveDecimals - ReserveConfiguration.DEBT_CEILING_DECIMALS))
                                .toUint128() <=
                              params.isolationModeDebtCeiling,
                            Errors.DEBT_CEILING_EXCEEDED
                          );
                        }
                        if (params.userEModeCategory != 0) {
                          require(
                            EModeConfiguration.isReserveEnabledOnBitmap(
                              eModeCategories[params.userEModeCategory].borrowableBitmap,
                              reservesData[params.asset].id
                            ),
                            Errors.NOT_BORROWABLE_IN_EMODE
                          );
                        }
                        (
                          vars.userCollateralInBaseCurrency,
                          vars.userDebtInBaseCurrency,
                          vars.currentLtv,
                          ,
                          vars.healthFactor,
                        ) = GenericLogic.calculateUserAccountData(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          DataTypes.CalculateUserAccountDataParams({
                            userConfig: params.userConfig,
                            reservesCount: params.reservesCount,
                            user: params.userAddress,
                            oracle: params.oracle,
                            userEModeCategory: params.userEModeCategory
                          })
                        );
                        require(vars.userCollateralInBaseCurrency != 0, Errors.COLLATERAL_BALANCE_IS_ZERO);
                        require(vars.currentLtv != 0, Errors.LTV_VALIDATION_FAILED);
                        require(
                          vars.healthFactor > HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                          Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                        );
                        vars.amountInBaseCurrency =
                          IPriceOracleGetter(params.oracle).getAssetPrice(params.asset) *
                          params.amount;
                        unchecked {
                          vars.amountInBaseCurrency /= vars.assetUnit;
                        }
                        //add the current already borrowed amount to the amount requested to calculate the total collateral needed.
                        vars.collateralNeededInBaseCurrency = (vars.userDebtInBaseCurrency + vars.amountInBaseCurrency)
                          .percentDiv(vars.currentLtv); //LTV is calculated in percentage
                        require(
                          vars.collateralNeededInBaseCurrency <= vars.userCollateralInBaseCurrency,
                          Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW
                        );
                        if (params.userConfig.isBorrowingAny()) {
                          (vars.siloedBorrowingEnabled, vars.siloedBorrowingAddress) = params
                            .userConfig
                            .getSiloedBorrowingState(reservesData, reservesList);
                          if (vars.siloedBorrowingEnabled) {
                            require(vars.siloedBorrowingAddress == params.asset, Errors.SILOED_BORROWING_VIOLATION);
                          } else {
                            require(
                              !params.reserveCache.reserveConfiguration.getSiloedBorrowing(),
                              Errors.SILOED_BORROWING_VIOLATION
                            );
                          }
                        }
                      }
                      /**
                       * @notice Validates a repay action.
                       * @param reserveCache The cached data of the reserve
                       * @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 debt The borrow balance of the user
                       */
                      function validateRepay(
                        DataTypes.ReserveCache memory reserveCache,
                        uint256 amountSent,
                        DataTypes.InterestRateMode interestRateMode,
                        address onBehalfOf,
                        uint256 debt
                      ) internal view {
                        require(amountSent != 0, Errors.INVALID_AMOUNT);
                        require(
                          interestRateMode == DataTypes.InterestRateMode.VARIABLE,
                          Errors.INVALID_INTEREST_RATE_MODE_SELECTED
                        );
                        require(
                          amountSent != type(uint256).max || msg.sender == onBehalfOf,
                          Errors.NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF
                        );
                        (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                        require(isActive, Errors.RESERVE_INACTIVE);
                        require(!isPaused, Errors.RESERVE_PAUSED);
                        require(debt != 0, Errors.NO_DEBT_OF_SELECTED_TYPE);
                      }
                      /**
                       * @notice Validates the action of setting an asset as collateral.
                       * @param reserveCache The cached data of the reserve
                       * @param userBalance The balance of the user
                       */
                      function validateSetUseReserveAsCollateral(
                        DataTypes.ReserveCache memory reserveCache,
                        uint256 userBalance
                      ) internal pure {
                        require(userBalance != 0, Errors.UNDERLYING_BALANCE_ZERO);
                        (bool isActive, , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                        require(isActive, Errors.RESERVE_INACTIVE);
                        require(!isPaused, Errors.RESERVE_PAUSED);
                      }
                      /**
                       * @notice Validates a flashloan action.
                       * @param reservesData The state of all the reserves
                       * @param assets The assets being flash-borrowed
                       * @param amounts The amounts for each asset being borrowed
                       */
                      function validateFlashloan(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        address[] memory assets,
                        uint256[] memory amounts
                      ) internal view {
                        require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS);
                        for (uint256 i = 0; i < assets.length; i++) {
                          for (uint256 j = i + 1; j < assets.length; j++) {
                            require(assets[i] != assets[j], Errors.INCONSISTENT_FLASHLOAN_PARAMS);
                          }
                          validateFlashloanSimple(reservesData[assets[i]], amounts[i]);
                        }
                      }
                      /**
                       * @notice Validates a flashloan action.
                       * @param reserve The state of the reserve
                       */
                      function validateFlashloanSimple(
                        DataTypes.ReserveData storage reserve,
                        uint256 amount
                      ) internal view {
                        DataTypes.ReserveConfigurationMap memory configuration = reserve.configuration;
                        require(!configuration.getPaused(), Errors.RESERVE_PAUSED);
                        require(configuration.getActive(), Errors.RESERVE_INACTIVE);
                        require(configuration.getFlashLoanEnabled(), Errors.FLASHLOAN_DISABLED);
                        require(
                          !configuration.getIsVirtualAccActive() ||
                            IERC20(reserve.aTokenAddress).totalSupply() >= amount,
                          Errors.INVALID_AMOUNT
                        );
                      }
                      struct ValidateLiquidationCallLocalVars {
                        bool collateralReserveActive;
                        bool collateralReservePaused;
                        bool principalReserveActive;
                        bool principalReservePaused;
                        bool isCollateralEnabled;
                      }
                      /**
                       * @notice Validates the liquidation action.
                       * @param userConfig The user configuration mapping
                       * @param collateralReserve The reserve data of the collateral
                       * @param debtReserve The reserve data of the debt
                       * @param params Additional parameters needed for the validation
                       */
                      function validateLiquidationCall(
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ReserveData storage collateralReserve,
                        DataTypes.ReserveData storage debtReserve,
                        DataTypes.ValidateLiquidationCallParams memory params
                      ) internal view {
                        ValidateLiquidationCallLocalVars memory vars;
                        (vars.collateralReserveActive, , , vars.collateralReservePaused) = collateralReserve
                          .configuration
                          .getFlags();
                        (vars.principalReserveActive, , , vars.principalReservePaused) = params
                          .debtReserveCache
                          .reserveConfiguration
                          .getFlags();
                        require(vars.collateralReserveActive && vars.principalReserveActive, Errors.RESERVE_INACTIVE);
                        require(!vars.collateralReservePaused && !vars.principalReservePaused, Errors.RESERVE_PAUSED);
                        require(
                          params.priceOracleSentinel == address(0) ||
                            params.healthFactor < MINIMUM_HEALTH_FACTOR_LIQUIDATION_THRESHOLD ||
                            IPriceOracleSentinel(params.priceOracleSentinel).isLiquidationAllowed(),
                          Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                        );
                        require(
                          collateralReserve.liquidationGracePeriodUntil < uint40(block.timestamp) &&
                            debtReserve.liquidationGracePeriodUntil < uint40(block.timestamp),
                          Errors.LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED
                        );
                        require(
                          params.healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                          Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
                        );
                        vars.isCollateralEnabled =
                          collateralReserve.configuration.getLiquidationThreshold() != 0 &&
                          userConfig.isUsingAsCollateral(collateralReserve.id);
                        //if collateral isn't enabled as collateral by user, it cannot be liquidated
                        require(vars.isCollateralEnabled, Errors.COLLATERAL_CANNOT_BE_LIQUIDATED);
                        require(params.totalDebt != 0, Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER);
                      }
                      /**
                       * @notice Validates the health factor of a user.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The state of the user for the specific reserve
                       * @param user The user to validate health factor of
                       * @param userEModeCategory The users active efficiency mode category
                       * @param reservesCount The number of available reserves
                       * @param oracle The price oracle
                       */
                      function validateHealthFactor(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap memory userConfig,
                        address user,
                        uint8 userEModeCategory,
                        uint256 reservesCount,
                        address oracle
                      ) internal view returns (uint256, bool) {
                        (, , , , uint256 healthFactor, bool hasZeroLtvCollateral) = GenericLogic
                          .calculateUserAccountData(
                            reservesData,
                            reservesList,
                            eModeCategories,
                            DataTypes.CalculateUserAccountDataParams({
                              userConfig: userConfig,
                              reservesCount: reservesCount,
                              user: user,
                              oracle: oracle,
                              userEModeCategory: userEModeCategory
                            })
                          );
                        require(
                          healthFactor >= HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                          Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                        );
                        return (healthFactor, hasZeroLtvCollateral);
                      }
                      /**
                       * @notice Validates the health factor of a user and the ltv of the asset being withdrawn.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param userConfig The state of the user for the specific reserve
                       * @param asset The asset for which the ltv will be validated
                       * @param from The user from which the aTokens are being transferred
                       * @param reservesCount The number of available reserves
                       * @param oracle The price oracle
                       * @param userEModeCategory The users active efficiency mode category
                       */
                      function validateHFAndLtv(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap memory userConfig,
                        address asset,
                        address from,
                        uint256 reservesCount,
                        address oracle,
                        uint8 userEModeCategory
                      ) internal view {
                        DataTypes.ReserveData memory reserve = reservesData[asset];
                        (, bool hasZeroLtvCollateral) = validateHealthFactor(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          userConfig,
                          from,
                          userEModeCategory,
                          reservesCount,
                          oracle
                        );
                        require(
                          !hasZeroLtvCollateral || reserve.configuration.getLtv() == 0,
                          Errors.LTV_VALIDATION_FAILED
                        );
                      }
                      /**
                       * @notice Validates a transfer action.
                       * @param reserve The reserve object
                       */
                      function validateTransfer(DataTypes.ReserveData storage reserve) internal view {
                        require(!reserve.configuration.getPaused(), Errors.RESERVE_PAUSED);
                      }
                      /**
                       * @notice Validates a drop reserve action.
                       * @param reservesList The addresses of all the active reserves
                       * @param reserve The reserve object
                       * @param asset The address of the reserve's underlying asset
                       */
                      function validateDropReserve(
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.ReserveData storage reserve,
                        address asset
                      ) internal view {
                        require(asset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                        require(reserve.id != 0 || reservesList[0] == asset, Errors.ASSET_NOT_LISTED);
                        require(
                          IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0,
                          Errors.VARIABLE_DEBT_SUPPLY_NOT_ZERO
                        );
                        require(
                          IERC20(reserve.aTokenAddress).totalSupply() == 0 && reserve.accruedToTreasury == 0,
                          Errors.UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO
                        );
                      }
                      /**
                       * @notice Validates the action of setting efficiency mode.
                       * @param eModeCategories a mapping storing configurations for all efficiency mode categories
                       * @param userConfig the user configuration
                       * @param reservesCount The total number of valid reserves
                       * @param categoryId The id of the category
                       */
                      function validateSetUserEMode(
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.UserConfigurationMap memory userConfig,
                        uint256 reservesCount,
                        uint8 categoryId
                      ) internal view {
                        DataTypes.EModeCategory storage eModeCategory = eModeCategories[categoryId];
                        // category is invalid if the liq threshold is not set
                        require(
                          categoryId == 0 || eModeCategory.liquidationThreshold != 0,
                          Errors.INCONSISTENT_EMODE_CATEGORY
                        );
                        // eMode can always be enabled if the user hasn't supplied anything
                        if (userConfig.isEmpty()) {
                          return;
                        }
                        // if user is trying to set another category than default we require that
                        // either the user is not borrowing, or it's borrowing assets of categoryId
                        if (categoryId != 0) {
                          unchecked {
                            for (uint256 i = 0; i < reservesCount; i++) {
                              if (userConfig.isBorrowing(i)) {
                                require(
                                  EModeConfiguration.isReserveEnabledOnBitmap(eModeCategory.borrowableBitmap, i),
                                  Errors.NOT_BORROWABLE_IN_EMODE
                                );
                              }
                            }
                          }
                        }
                      }
                      /**
                       * @notice Validates the action of activating the asset as collateral.
                       * @dev Only possible if the asset has non-zero LTV and the user is not in isolation mode
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig the user configuration
                       * @param reserveConfig The reserve configuration
                       * @return True if the asset can be activated as collateral, false otherwise
                       */
                      function validateUseAsCollateral(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ReserveConfigurationMap memory reserveConfig
                      ) internal view returns (bool) {
                        if (reserveConfig.getLtv() == 0) {
                          return false;
                        }
                        if (!userConfig.isUsingAsCollateralAny()) {
                          return true;
                        }
                        (bool isolationModeActive, , ) = userConfig.getIsolationModeState(reservesData, reservesList);
                        return (!isolationModeActive && reserveConfig.getDebtCeiling() == 0);
                      }
                      /**
                       * @notice Validates if an asset should be automatically activated as collateral in the following actions: supply,
                       * transfer, mint unbacked, and liquidate
                       * @dev This is used to ensure that isolated assets are not enabled as collateral automatically
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param userConfig the user configuration
                       * @param reserveConfig The reserve configuration
                       * @return True if the asset can be activated as collateral, false otherwise
                       */
                      function validateAutomaticUseAsCollateral(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ReserveConfigurationMap memory reserveConfig,
                        address aTokenAddress
                      ) internal view returns (bool) {
                        if (reserveConfig.getDebtCeiling() != 0) {
                          // ensures only the ISOLATED_COLLATERAL_SUPPLIER_ROLE can enable collateral as side-effect of an action
                          IPoolAddressesProvider addressesProvider = IncentivizedERC20(aTokenAddress)
                            .POOL()
                            .ADDRESSES_PROVIDER();
                          if (
                            !IAccessControl(addressesProvider.getACLManager()).hasRole(
                              ISOLATED_COLLATERAL_SUPPLIER_ROLE,
                              msg.sender
                            )
                          ) return false;
                        }
                        return validateUseAsCollateral(reservesData, reservesList, userConfig, reserveConfig);
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.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';
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    /**
                     * @title ReserveLogic library
                     * @author Aave
                     * @notice Implements the logic to update the reserves state
                     */
                    library ReserveLogic {
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using SafeCast for uint256;
                      using GPv2SafeERC20 for IERC20;
                      using ReserveLogic for DataTypes.ReserveData;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      // See `IPool` for descriptions
                      event ReserveDataUpdated(
                        address indexed reserve,
                        uint256 liquidityRate,
                        uint256 stableBorrowRate,
                        uint256 variableBorrowRate,
                        uint256 liquidityIndex,
                        uint256 variableBorrowIndex
                      );
                      /**
                       * @notice Returns the ongoing normalized income for the reserve.
                       * @dev A value of 1e27 means there is no income. As time passes, the income is accrued
                       * @dev 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 == block.timestamp) {
                          //if the index was updated in the same block, no need to perform any calculation
                          return reserve.liquidityIndex;
                        } else {
                          return
                            MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul(
                              reserve.liquidityIndex
                            );
                        }
                      }
                      /**
                       * @notice Returns the ongoing normalized variable debt for the reserve.
                       * @dev A value of 1e27 means there is no debt. As time passes, the debt is accrued
                       * @dev 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 == block.timestamp) {
                          //if the index was updated in the same block, no need to perform any calculation
                          return reserve.variableBorrowIndex;
                        } else {
                          return
                            MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul(
                              reserve.variableBorrowIndex
                            );
                        }
                      }
                      /**
                       * @notice Updates the liquidity cumulative index and the variable borrow index.
                       * @param reserve The reserve object
                       * @param reserveCache The caching layer for the reserve data
                       */
                      function updateState(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.ReserveCache memory reserveCache
                      ) internal {
                        // If time didn't pass since last stored timestamp, skip state update
                        //solium-disable-next-line
                        if (reserve.lastUpdateTimestamp == uint40(block.timestamp)) {
                          return;
                        }
                        _updateIndexes(reserve, reserveCache);
                        _accrueToTreasury(reserve, reserveCache);
                        //solium-disable-next-line
                        reserve.lastUpdateTimestamp = uint40(block.timestamp);
                      }
                      /**
                       * @notice 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 suppliers.
                       * @param reserve The reserve object
                       * @param totalLiquidity The total liquidity available in the reserve
                       * @param amount The amount to accumulate
                       * @return The next liquidity index of the reserve
                       */
                      function cumulateToLiquidityIndex(
                        DataTypes.ReserveData storage reserve,
                        uint256 totalLiquidity,
                        uint256 amount
                      ) internal returns (uint256) {
                        //next liquidity index is calculated this way: `((amount / totalLiquidity) + 1) * liquidityIndex`
                        //division `amount / totalLiquidity` done in ray for precision
                        uint256 result = (amount.wadToRay().rayDiv(totalLiquidity.wadToRay()) + WadRayMath.RAY).rayMul(
                          reserve.liquidityIndex
                        );
                        reserve.liquidityIndex = result.toUint128();
                        return result;
                      }
                      /**
                       * @notice Initializes a reserve.
                       * @param reserve The reserve object
                       * @param aTokenAddress The address of the overlying atoken contract
                       * @param variableDebtTokenAddress The address of the overlying variable debt token contract
                       * @param interestRateStrategyAddress The address of the interest rate strategy contract
                       */
                      function init(
                        DataTypes.ReserveData storage reserve,
                        address aTokenAddress,
                        address variableDebtTokenAddress,
                        address interestRateStrategyAddress
                      ) internal {
                        require(reserve.aTokenAddress == address(0), Errors.RESERVE_ALREADY_INITIALIZED);
                        reserve.liquidityIndex = uint128(WadRayMath.RAY);
                        reserve.variableBorrowIndex = uint128(WadRayMath.RAY);
                        reserve.aTokenAddress = aTokenAddress;
                        reserve.variableDebtTokenAddress = variableDebtTokenAddress;
                        reserve.interestRateStrategyAddress = interestRateStrategyAddress;
                      }
                      /**
                       * @notice Updates the reserve current variable borrow rate and the current liquidity rate.
                       * @param reserve The reserve reserve to be updated
                       * @param reserveCache The caching layer for the reserve data
                       * @param reserveAddress The address of the reserve to be updated
                       * @param liquidityAdded The amount of liquidity added to the protocol (supply or repay) in the previous action
                       * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow)
                       */
                      function updateInterestRatesAndVirtualBalance(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.ReserveCache memory reserveCache,
                        address reserveAddress,
                        uint256 liquidityAdded,
                        uint256 liquidityTaken
                      ) internal {
                        uint256 totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul(
                          reserveCache.nextVariableBorrowIndex
                        );
                        (uint256 nextLiquidityRate, uint256 nextVariableRate) = IReserveInterestRateStrategy(
                          reserve.interestRateStrategyAddress
                        ).calculateInterestRates(
                            DataTypes.CalculateInterestRatesParams({
                              unbacked: reserve.unbacked,
                              liquidityAdded: liquidityAdded,
                              liquidityTaken: liquidityTaken,
                              totalDebt: totalVariableDebt,
                              reserveFactor: reserveCache.reserveFactor,
                              reserve: reserveAddress,
                              usingVirtualBalance: reserve.configuration.getIsVirtualAccActive(),
                              virtualUnderlyingBalance: reserve.virtualUnderlyingBalance
                            })
                          );
                        reserve.currentLiquidityRate = nextLiquidityRate.toUint128();
                        reserve.currentVariableBorrowRate = nextVariableRate.toUint128();
                        // Only affect virtual balance if the reserve uses it
                        if (reserve.configuration.getIsVirtualAccActive()) {
                          if (liquidityAdded > 0) {
                            reserve.virtualUnderlyingBalance += liquidityAdded.toUint128();
                          }
                          if (liquidityTaken > 0) {
                            reserve.virtualUnderlyingBalance -= liquidityTaken.toUint128();
                          }
                        }
                        emit ReserveDataUpdated(
                          reserveAddress,
                          nextLiquidityRate,
                          0,
                          nextVariableRate,
                          reserveCache.nextLiquidityIndex,
                          reserveCache.nextVariableBorrowIndex
                        );
                      }
                      /**
                       * @notice Mints part of the repaid interest to the reserve treasury as a function of the reserve factor for the
                       * specific asset.
                       * @param reserve The reserve to be updated
                       * @param reserveCache The caching layer for the reserve data
                       */
                      function _accrueToTreasury(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.ReserveCache memory reserveCache
                      ) internal {
                        if (reserveCache.reserveFactor == 0) {
                          return;
                        }
                        //calculate the total variable debt at moment of the last interaction
                        uint256 prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                          reserveCache.currVariableBorrowIndex
                        );
                        //calculate the new total variable debt after accumulation of the interest on the index
                        uint256 currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                          reserveCache.nextVariableBorrowIndex
                        );
                        //debt accrued is the sum of the current debt minus the sum of the debt at the last update
                        uint256 totalDebtAccrued = currTotalVariableDebt - prevTotalVariableDebt;
                        uint256 amountToMint = totalDebtAccrued.percentMul(reserveCache.reserveFactor);
                        if (amountToMint != 0) {
                          reserve.accruedToTreasury += amountToMint.rayDiv(reserveCache.nextLiquidityIndex).toUint128();
                        }
                      }
                      /**
                       * @notice Updates the reserve indexes and the timestamp of the update.
                       * @param reserve The reserve reserve to be updated
                       * @param reserveCache The cache layer holding the cached protocol data
                       */
                      function _updateIndexes(
                        DataTypes.ReserveData storage reserve,
                        DataTypes.ReserveCache memory reserveCache
                      ) internal {
                        // Only cumulating on the supply side if there is any income being produced
                        // The case of Reserve Factor 100% is not a problem (currentLiquidityRate == 0),
                        // as liquidity index should not be updated
                        if (reserveCache.currLiquidityRate != 0) {
                          uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(
                            reserveCache.currLiquidityRate,
                            reserveCache.reserveLastUpdateTimestamp
                          );
                          reserveCache.nextLiquidityIndex = cumulatedLiquidityInterest.rayMul(
                            reserveCache.currLiquidityIndex
                          );
                          reserve.liquidityIndex = reserveCache.nextLiquidityIndex.toUint128();
                        }
                        // Variable borrow index only gets updated if there is any variable debt.
                        // reserveCache.currVariableBorrowRate != 0 is not a correct validation,
                        // because a positive base variable rate can be stored on
                        // reserveCache.currVariableBorrowRate, but the index should not increase
                        if (reserveCache.currScaledVariableDebt != 0) {
                          uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(
                            reserveCache.currVariableBorrowRate,
                            reserveCache.reserveLastUpdateTimestamp
                          );
                          reserveCache.nextVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(
                            reserveCache.currVariableBorrowIndex
                          );
                          reserve.variableBorrowIndex = reserveCache.nextVariableBorrowIndex.toUint128();
                        }
                      }
                      /**
                       * @notice Creates a cache object to avoid repeated storage reads and external contract calls when updating state and
                       * interest rates.
                       * @param reserve The reserve object for which the cache will be filled
                       * @return The cache object
                       */
                      function cache(
                        DataTypes.ReserveData storage reserve
                      ) internal view returns (DataTypes.ReserveCache memory) {
                        DataTypes.ReserveCache memory reserveCache;
                        reserveCache.reserveConfiguration = reserve.configuration;
                        reserveCache.reserveFactor = reserveCache.reserveConfiguration.getReserveFactor();
                        reserveCache.currLiquidityIndex = reserveCache.nextLiquidityIndex = reserve.liquidityIndex;
                        reserveCache.currVariableBorrowIndex = reserveCache.nextVariableBorrowIndex = reserve
                          .variableBorrowIndex;
                        reserveCache.currLiquidityRate = reserve.currentLiquidityRate;
                        reserveCache.currVariableBorrowRate = reserve.currentVariableBorrowRate;
                        reserveCache.aTokenAddress = reserve.aTokenAddress;
                        reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
                        reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp;
                        reserveCache.currScaledVariableDebt = reserveCache.nextScaledVariableDebt = IVariableDebtToken(
                          reserveCache.variableDebtTokenAddress
                        ).scaledTotalSupply();
                        return reserveCache;
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    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 internal constant LTV_MASK =                       0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore
                      uint256 internal constant LIQUIDATION_THRESHOLD_MASK =     0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore
                      uint256 internal constant LIQUIDATION_BONUS_MASK =         0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore
                      uint256 internal constant DECIMALS_MASK =                  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant ACTIVE_MASK =                    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant FROZEN_MASK =                    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant BORROWING_MASK =                 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant PAUSED_MASK =                    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant BORROWABLE_IN_ISOLATION_MASK =   0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant SILOED_BORROWING_MASK =          0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant FLASHLOAN_ENABLED_MASK =         0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant RESERVE_FACTOR_MASK =            0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant BORROW_CAP_MASK =                0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant SUPPLY_CAP_MASK =                0xFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant LIQUIDATION_PROTOCOL_FEE_MASK =  0xFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      //@notice there is an unoccupied hole of 8 bits from 168 to 176 left from pre 3.2 eModeCategory
                      uint256 internal constant UNBACKED_MINT_CAP_MASK =         0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant DEBT_CEILING_MASK =              0xF0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      uint256 internal constant VIRTUAL_ACC_ACTIVE_MASK =        0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                      /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed
                      uint256 internal constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16;
                      uint256 internal constant LIQUIDATION_BONUS_START_BIT_POSITION = 32;
                      uint256 internal constant RESERVE_DECIMALS_START_BIT_POSITION = 48;
                      uint256 internal constant IS_ACTIVE_START_BIT_POSITION = 56;
                      uint256 internal constant IS_FROZEN_START_BIT_POSITION = 57;
                      uint256 internal constant BORROWING_ENABLED_START_BIT_POSITION = 58;
                      uint256 internal constant IS_PAUSED_START_BIT_POSITION = 60;
                      uint256 internal constant BORROWABLE_IN_ISOLATION_START_BIT_POSITION = 61;
                      uint256 internal constant SILOED_BORROWING_START_BIT_POSITION = 62;
                      uint256 internal constant FLASHLOAN_ENABLED_START_BIT_POSITION = 63;
                      uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64;
                      uint256 internal constant BORROW_CAP_START_BIT_POSITION = 80;
                      uint256 internal constant SUPPLY_CAP_START_BIT_POSITION = 116;
                      uint256 internal constant LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION = 152;
                      //@notice there is an unoccupied hole of 8 bits from 168 to 176 left from pre 3.2 eModeCategory
                      uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176;
                      uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212;
                      uint256 internal constant VIRTUAL_ACC_START_BIT_POSITION = 252;
                      uint256 internal constant MAX_VALID_LTV = 65535;
                      uint256 internal constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535;
                      uint256 internal constant MAX_VALID_LIQUIDATION_BONUS = 65535;
                      uint256 internal constant MAX_VALID_DECIMALS = 255;
                      uint256 internal constant MAX_VALID_RESERVE_FACTOR = 65535;
                      uint256 internal constant MAX_VALID_BORROW_CAP = 68719476735;
                      uint256 internal constant MAX_VALID_SUPPLY_CAP = 68719476735;
                      uint256 internal constant MAX_VALID_LIQUIDATION_PROTOCOL_FEE = 65535;
                      uint256 internal constant MAX_VALID_UNBACKED_MINT_CAP = 68719476735;
                      uint256 internal constant MAX_VALID_DEBT_CEILING = 1099511627775;
                      uint256 public constant DEBT_CEILING_DECIMALS = 2;
                      uint16 public constant MAX_RESERVES_COUNT = 128;
                      /**
                       * @notice 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.INVALID_LTV);
                        self.data = (self.data & LTV_MASK) | ltv;
                      }
                      /**
                       * @notice Gets the Loan to Value of the reserve
                       * @param self The reserve configuration
                       * @return The loan to value
                       */
                      function getLtv(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) {
                        return self.data & ~LTV_MASK;
                      }
                      /**
                       * @notice 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.INVALID_LIQ_THRESHOLD);
                        self.data =
                          (self.data & LIQUIDATION_THRESHOLD_MASK) |
                          (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the liquidation threshold of the reserve
                       * @param self The reserve configuration
                       * @return The liquidation threshold
                       */
                      function getLiquidationThreshold(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION;
                      }
                      /**
                       * @notice 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.INVALID_LIQ_BONUS);
                        self.data =
                          (self.data & LIQUIDATION_BONUS_MASK) |
                          (bonus << LIQUIDATION_BONUS_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the liquidation bonus of the reserve
                       * @param self The reserve configuration
                       * @return The liquidation bonus
                       */
                      function getLiquidationBonus(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION;
                      }
                      /**
                       * @notice 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.INVALID_DECIMALS);
                        self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION);
                      }
                      /**
                       * @notice 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 memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
                      }
                      /**
                       * @notice 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);
                      }
                      /**
                       * @notice Gets the active state of the reserve
                       * @param self The reserve configuration
                       * @return The active state
                       */
                      function getActive(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                        return (self.data & ~ACTIVE_MASK) != 0;
                      }
                      /**
                       * @notice 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);
                      }
                      /**
                       * @notice Gets the frozen state of the reserve
                       * @param self The reserve configuration
                       * @return The frozen state
                       */
                      function getFrozen(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                        return (self.data & ~FROZEN_MASK) != 0;
                      }
                      /**
                       * @notice Sets the paused state of the reserve
                       * @param self The reserve configuration
                       * @param paused The paused state
                       */
                      function setPaused(DataTypes.ReserveConfigurationMap memory self, bool paused) internal pure {
                        self.data =
                          (self.data & PAUSED_MASK) |
                          (uint256(paused ? 1 : 0) << IS_PAUSED_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the paused state of the reserve
                       * @param self The reserve configuration
                       * @return The paused state
                       */
                      function getPaused(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) {
                        return (self.data & ~PAUSED_MASK) != 0;
                      }
                      /**
                       * @notice Sets the borrowable in isolation flag for the reserve.
                       * @dev When this flag is set to true, the asset will be borrowable against isolated collaterals and the borrowed
                       * amount will be accumulated in the isolated collateral's total debt exposure.
                       * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
                       * consistency in the debt ceiling calculations.
                       * @param self The reserve configuration
                       * @param borrowable True if the asset is borrowable
                       */
                      function setBorrowableInIsolation(
                        DataTypes.ReserveConfigurationMap memory self,
                        bool borrowable
                      ) internal pure {
                        self.data =
                          (self.data & BORROWABLE_IN_ISOLATION_MASK) |
                          (uint256(borrowable ? 1 : 0) << BORROWABLE_IN_ISOLATION_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the borrowable in isolation flag for the reserve.
                       * @dev If the returned flag is true, the asset is borrowable against isolated collateral. Assets borrowed with
                       * isolated collateral is accounted for in the isolated collateral's total debt exposure.
                       * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep
                       * consistency in the debt ceiling calculations.
                       * @param self The reserve configuration
                       * @return The borrowable in isolation flag
                       */
                      function getBorrowableInIsolation(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~BORROWABLE_IN_ISOLATION_MASK) != 0;
                      }
                      /**
                       * @notice Sets the siloed borrowing flag for the reserve.
                       * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
                       * @param self The reserve configuration
                       * @param siloed True if the asset is siloed
                       */
                      function setSiloedBorrowing(
                        DataTypes.ReserveConfigurationMap memory self,
                        bool siloed
                      ) internal pure {
                        self.data =
                          (self.data & SILOED_BORROWING_MASK) |
                          (uint256(siloed ? 1 : 0) << SILOED_BORROWING_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the siloed borrowing flag for the reserve.
                       * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset.
                       * @param self The reserve configuration
                       * @return The siloed borrowing flag
                       */
                      function getSiloedBorrowing(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~SILOED_BORROWING_MASK) != 0;
                      }
                      /**
                       * @notice 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);
                      }
                      /**
                       * @notice Gets the borrowing state of the reserve
                       * @param self The reserve configuration
                       * @return The borrowing state
                       */
                      function getBorrowingEnabled(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~BORROWING_MASK) != 0;
                      }
                      /**
                       * @notice 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.INVALID_RESERVE_FACTOR);
                        self.data =
                          (self.data & RESERVE_FACTOR_MASK) |
                          (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the reserve factor of the reserve
                       * @param self The reserve configuration
                       * @return The reserve factor
                       */
                      function getReserveFactor(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the borrow cap of the reserve
                       * @param self The reserve configuration
                       * @param borrowCap The borrow cap
                       */
                      function setBorrowCap(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 borrowCap
                      ) internal pure {
                        require(borrowCap <= MAX_VALID_BORROW_CAP, Errors.INVALID_BORROW_CAP);
                        self.data = (self.data & BORROW_CAP_MASK) | (borrowCap << BORROW_CAP_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the borrow cap of the reserve
                       * @param self The reserve configuration
                       * @return The borrow cap
                       */
                      function getBorrowCap(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the supply cap of the reserve
                       * @param self The reserve configuration
                       * @param supplyCap The supply cap
                       */
                      function setSupplyCap(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 supplyCap
                      ) internal pure {
                        require(supplyCap <= MAX_VALID_SUPPLY_CAP, Errors.INVALID_SUPPLY_CAP);
                        self.data = (self.data & SUPPLY_CAP_MASK) | (supplyCap << SUPPLY_CAP_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the supply cap of the reserve
                       * @param self The reserve configuration
                       * @return The supply cap
                       */
                      function getSupplyCap(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the debt ceiling in isolation mode for the asset
                       * @param self The reserve configuration
                       * @param ceiling The maximum debt ceiling for the asset
                       */
                      function setDebtCeiling(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 ceiling
                      ) internal pure {
                        require(ceiling <= MAX_VALID_DEBT_CEILING, Errors.INVALID_DEBT_CEILING);
                        self.data = (self.data & DEBT_CEILING_MASK) | (ceiling << DEBT_CEILING_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the debt ceiling for the asset if the asset is in isolation mode
                       * @param self The reserve configuration
                       * @return The debt ceiling (0 = isolation mode disabled)
                       */
                      function getDebtCeiling(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~DEBT_CEILING_MASK) >> DEBT_CEILING_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the liquidation protocol fee of the reserve
                       * @param self The reserve configuration
                       * @param liquidationProtocolFee The liquidation protocol fee
                       */
                      function setLiquidationProtocolFee(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 liquidationProtocolFee
                      ) internal pure {
                        require(
                          liquidationProtocolFee <= MAX_VALID_LIQUIDATION_PROTOCOL_FEE,
                          Errors.INVALID_LIQUIDATION_PROTOCOL_FEE
                        );
                        self.data =
                          (self.data & LIQUIDATION_PROTOCOL_FEE_MASK) |
                          (liquidationProtocolFee << LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION);
                      }
                      /**
                       * @dev Gets the liquidation protocol fee
                       * @param self The reserve configuration
                       * @return The liquidation protocol fee
                       */
                      function getLiquidationProtocolFee(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return
                          (self.data & ~LIQUIDATION_PROTOCOL_FEE_MASK) >> LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the unbacked mint cap of the reserve
                       * @param self The reserve configuration
                       * @param unbackedMintCap The unbacked mint cap
                       */
                      function setUnbackedMintCap(
                        DataTypes.ReserveConfigurationMap memory self,
                        uint256 unbackedMintCap
                      ) internal pure {
                        require(unbackedMintCap <= MAX_VALID_UNBACKED_MINT_CAP, Errors.INVALID_UNBACKED_MINT_CAP);
                        self.data =
                          (self.data & UNBACKED_MINT_CAP_MASK) |
                          (unbackedMintCap << UNBACKED_MINT_CAP_START_BIT_POSITION);
                      }
                      /**
                       * @dev Gets the unbacked mint cap of the reserve
                       * @param self The reserve configuration
                       * @return The unbacked mint cap
                       */
                      function getUnbackedMintCap(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256) {
                        return (self.data & ~UNBACKED_MINT_CAP_MASK) >> UNBACKED_MINT_CAP_START_BIT_POSITION;
                      }
                      /**
                       * @notice Sets the flashloanable flag for the reserve
                       * @param self The reserve configuration
                       * @param flashLoanEnabled True if the asset is flashloanable, false otherwise
                       */
                      function setFlashLoanEnabled(
                        DataTypes.ReserveConfigurationMap memory self,
                        bool flashLoanEnabled
                      ) internal pure {
                        self.data =
                          (self.data & FLASHLOAN_ENABLED_MASK) |
                          (uint256(flashLoanEnabled ? 1 : 0) << FLASHLOAN_ENABLED_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the flashloanable flag for the reserve
                       * @param self The reserve configuration
                       * @return The flashloanable flag
                       */
                      function getFlashLoanEnabled(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~FLASHLOAN_ENABLED_MASK) != 0;
                      }
                      /**
                       * @notice Sets the virtual account active/not state of the reserve
                       * @param self The reserve configuration
                       * @param active The active state
                       */
                      function setVirtualAccActive(
                        DataTypes.ReserveConfigurationMap memory self,
                        bool active
                      ) internal pure {
                        self.data =
                          (self.data & VIRTUAL_ACC_ACTIVE_MASK) |
                          (uint256(active ? 1 : 0) << VIRTUAL_ACC_START_BIT_POSITION);
                      }
                      /**
                       * @notice Gets the virtual account active/not state of the reserve
                       * @dev The state should be true for all normal assets and should be false
                       *  only in special cases (ex. GHO) where an asset is minted instead of supplied.
                       * @param self The reserve configuration
                       * @return The active state
                       */
                      function getIsVirtualAccActive(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool) {
                        return (self.data & ~VIRTUAL_ACC_ACTIVE_MASK) != 0;
                      }
                      /**
                       * @notice Gets the configuration flags of the reserve
                       * @param self The reserve configuration
                       * @return The state flag representing active
                       * @return The state flag representing frozen
                       * @return The state flag representing borrowing enabled
                       * @return The state flag representing paused
                       */
                      function getFlags(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (bool, bool, bool, bool) {
                        uint256 dataLocal = self.data;
                        return (
                          (dataLocal & ~ACTIVE_MASK) != 0,
                          (dataLocal & ~FROZEN_MASK) != 0,
                          (dataLocal & ~BORROWING_MASK) != 0,
                          (dataLocal & ~PAUSED_MASK) != 0
                        );
                      }
                      /**
                       * @notice Gets the configuration parameters of the reserve from storage
                       * @param self The reserve configuration
                       * @return The state param representing ltv
                       * @return The state param representing liquidation threshold
                       * @return The state param representing liquidation bonus
                       * @return The state param representing reserve decimals
                       * @return The state param representing reserve factor
                       */
                      function getParams(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure 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
                        );
                      }
                      /**
                       * @notice Gets the caps parameters of the reserve from storage
                       * @param self The reserve configuration
                       * @return The state param representing borrow cap
                       * @return The state param representing supply cap.
                       */
                      function getCaps(
                        DataTypes.ReserveConfigurationMap memory self
                      ) internal pure returns (uint256, uint256) {
                        uint256 dataLocal = self.data;
                        return (
                          (dataLocal & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION,
                          (dataLocal & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION
                        );
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IScaledBalanceToken
                     * @author Aave
                     * @notice Defines the basic interface for a scaled-balance token.
                     */
                    interface IScaledBalanceToken {
                      /**
                       * @dev Emitted after the mint action
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the minted tokens
                       * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
                       * @param index The next liquidity index of the reserve
                       */
                      event Mint(
                        address indexed caller,
                        address indexed onBehalfOf,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @dev Emitted after the burn action
                       * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
                       * @param from The address from which the tokens will be burned
                       * @param target The address that will receive the underlying, if any
                       * @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
                       * @param index The next liquidity index of the reserve
                       */
                      event Burn(
                        address indexed from,
                        address indexed target,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @notice Returns the scaled balance of the user.
                       * @dev 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);
                      /**
                       * @notice 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 total supply
                       */
                      function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                      /**
                       * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
                       * @return The scaled total supply
                       */
                      function scaledTotalSupply() external view returns (uint256);
                      /**
                       * @notice Returns last index interest was accrued to the user's balance
                       * @param user The address of the user
                       * @return The last index interest was accrued to the user's balance, expressed in ray
                       */
                      function getPreviousIndex(address user) external view returns (uint256);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                    import {IPool} from './IPool.sol';
                    /**
                     * @title IInitializableAToken
                     * @author Aave
                     * @notice Interface for the initialize function on AToken
                     */
                    interface IInitializableAToken {
                      /**
                       * @dev Emitted when an aToken is initialized
                       * @param underlyingAsset The address of the underlying asset
                       * @param pool The address of the associated 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
                      );
                      /**
                       * @notice Initializes the aToken
                       * @param pool The pool contract that is initializing this contract
                       * @param treasury The address of the Aave treasury, receiving the fees on this aToken
                       * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @param incentivesController The smart contract managing potential incentives distribution
                       * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's
                       * @param aTokenName The name of the aToken
                       * @param aTokenSymbol The symbol of the aToken
                       * @param params A set of encoded parameters for additional initialization
                       */
                      function initialize(
                        IPool pool,
                        address treasury,
                        address underlyingAsset,
                        IAaveIncentivesController incentivesController,
                        uint8 aTokenDecimals,
                        string calldata aTokenName,
                        string calldata aTokenSymbol,
                        bytes calldata params
                      ) external;
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
                    pragma solidity ^0.8.0;
                    /**
                     * @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) {
                        // This method relies on extcodesize, which returns 0 for contracts in
                        // construction, since the code is only stored at the end of the
                        // constructor execution.
                        uint256 size;
                        assembly {
                          size := extcodesize(account)
                        }
                        return size > 0;
                      }
                      /**
                       * @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');
                        (bool success, ) = recipient.call{value: amount}('');
                        require(success, 'Address: unable to send value, recipient may have reverted');
                      }
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCall(target, data, 'Address: low-level call failed');
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                       * `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                      ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but also transferring `value` wei to `target`.
                       *
                       * Requirements:
                       *
                       * - the calling contract must have an ETH balance of at least `value`.
                       * - the called Solidity function must be `payable`.
                       *
                       * _Available since v3.1._
                       */
                      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');
                      }
                      /**
                       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                       * with `errorMessage` as a fallback revert reason when `target` reverts.
                       *
                       * _Available since v3.1._
                       */
                      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');
                        require(isContract(target), 'Address: call to non-contract');
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(
                        address target,
                        bytes memory data
                      ) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, 'Address: low-level static call failed');
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a static call.
                       *
                       * _Available since v3.3._
                       */
                      function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                      ) internal view returns (bytes memory) {
                        require(isContract(target), 'Address: static call to non-contract');
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, 'Address: low-level delegate call failed');
                      }
                      /**
                       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                       * but performing a delegate call.
                       *
                       * _Available since v3.4._
                       */
                      function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                      ) internal returns (bytes memory) {
                        require(isContract(target), 'Address: delegate call to non-contract');
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                      }
                      /**
                       * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                       * revert reason using the provided one.
                       *
                       * _Available since v4.3._
                       */
                      function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                      ) internal pure returns (bytes memory) {
                        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
                            assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                            }
                          } else {
                            revert(errorMessage);
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                    /**
                     * @title IReserveInterestRateStrategy
                     * @author BGD Labs
                     * @notice Basic interface for any rate strategy used by the Aave protocol
                     */
                    interface IReserveInterestRateStrategy {
                      /**
                       * @notice Sets interest rate data for an Aave rate strategy
                       * @param reserve The reserve to update
                       * @param rateData The abi encoded reserve interest rate data to apply to the given reserve
                       *   Abstracted this way as rate strategies can be custom
                       */
                      function setInterestRateParams(address reserve, bytes calldata rateData) external;
                      /**
                       * @notice Calculates the interest rates depending on the reserve's state and configurations
                       * @param params The parameters needed to calculate interest rates
                       * @return liquidityRate The liquidity rate expressed in ray
                       * @return variableBorrowRate The variable borrow rate expressed in ray
                       */
                      function calculateInterestRates(
                        DataTypes.CalculateInterestRatesParams memory params
                      ) external view returns (uint256, uint256);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IPriceOracleGetter
                     * @author Aave
                     * @notice Interface for the Aave price oracle.
                     */
                    interface IPriceOracleGetter {
                      /**
                       * @notice Returns the base currency address
                       * @dev Address 0x0 is reserved for USD as base currency.
                       * @return Returns the base currency address.
                       */
                      function BASE_CURRENCY() external view returns (address);
                      /**
                       * @notice Returns the base currency unit
                       * @dev 1 ether for ETH, 1e8 for USD.
                       * @return Returns the base currency unit.
                       */
                      function BASE_CURRENCY_UNIT() external view returns (uint256);
                      /**
                       * @notice Returns the asset price in the base currency
                       * @param asset The address of the asset
                       * @return The price of the asset
                       */
                      function getAssetPrice(address asset) external view returns (uint256);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    /**
                     * @title IPriceOracleSentinel
                     * @author Aave
                     * @notice Defines the basic interface for the PriceOracleSentinel
                     */
                    interface IPriceOracleSentinel {
                      /**
                       * @dev Emitted after the sequencer oracle is updated
                       * @param newSequencerOracle The new sequencer oracle
                       */
                      event SequencerOracleUpdated(address newSequencerOracle);
                      /**
                       * @dev Emitted after the grace period is updated
                       * @param newGracePeriod The new grace period value
                       */
                      event GracePeriodUpdated(uint256 newGracePeriod);
                      /**
                       * @notice Returns the PoolAddressesProvider
                       * @return The address of the PoolAddressesProvider contract
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Returns true if the `borrow` operation is allowed.
                       * @dev Operation not allowed when PriceOracle is down or grace period not passed.
                       * @return True if the `borrow` operation is allowed, false otherwise.
                       */
                      function isBorrowAllowed() external view returns (bool);
                      /**
                       * @notice Returns true if the `liquidation` operation is allowed.
                       * @dev Operation not allowed when PriceOracle is down or grace period not passed.
                       * @return True if the `liquidation` operation is allowed, false otherwise.
                       */
                      function isLiquidationAllowed() external view returns (bool);
                      /**
                       * @notice Updates the address of the sequencer oracle
                       * @param newSequencerOracle The address of the new Sequencer Oracle to use
                       */
                      function setSequencerOracle(address newSequencerOracle) external;
                      /**
                       * @notice Updates the duration of the grace period
                       * @param newGracePeriod The value of the new grace period duration
                       */
                      function setGracePeriod(uint256 newGracePeriod) external;
                      /**
                       * @notice Returns the SequencerOracle
                       * @return The address of the sequencer oracle contract
                       */
                      function getSequencerOracle() external view returns (address);
                      /**
                       * @notice Returns the grace period
                       * @return The duration of the grace period
                       */
                      function getGracePeriod() external view returns (uint256);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IPoolAddressesProvider
                     * @author Aave
                     * @notice Defines the basic interface for a Pool Addresses Provider.
                     */
                    interface IPoolAddressesProvider {
                      /**
                       * @dev Emitted when the market identifier is updated.
                       * @param oldMarketId The old id of the market
                       * @param newMarketId The new id of the market
                       */
                      event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                      /**
                       * @dev Emitted when the pool is updated.
                       * @param oldAddress The old address of the Pool
                       * @param newAddress The new address of the Pool
                       */
                      event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool configurator is updated.
                       * @param oldAddress The old address of the PoolConfigurator
                       * @param newAddress The new address of the PoolConfigurator
                       */
                      event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle is updated.
                       * @param oldAddress The old address of the PriceOracle
                       * @param newAddress The new address of the PriceOracle
                       */
                      event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL manager is updated.
                       * @param oldAddress The old address of the ACLManager
                       * @param newAddress The new address of the ACLManager
                       */
                      event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL admin is updated.
                       * @param oldAddress The old address of the ACLAdmin
                       * @param newAddress The new address of the ACLAdmin
                       */
                      event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle sentinel is updated.
                       * @param oldAddress The old address of the PriceOracleSentinel
                       * @param newAddress The new address of the PriceOracleSentinel
                       */
                      event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool data provider is updated.
                       * @param oldAddress The old address of the PoolDataProvider
                       * @param newAddress The new address of the PoolDataProvider
                       */
                      event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when a new proxy is created.
                       * @param id The identifier of the proxy
                       * @param proxyAddress The address of the created proxy contract
                       * @param implementationAddress The address of the implementation contract
                       */
                      event ProxyCreated(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address indexed implementationAddress
                      );
                      /**
                       * @dev Emitted when a new non-proxied contract address is registered.
                       * @param id The identifier of the contract
                       * @param oldAddress The address of the old contract
                       * @param newAddress The address of the new contract
                       */
                      event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the implementation of the proxy registered with id is updated
                       * @param id The identifier of the contract
                       * @param proxyAddress The address of the proxy contract
                       * @param oldImplementationAddress The address of the old implementation contract
                       * @param newImplementationAddress The address of the new implementation contract
                       */
                      event AddressSetAsProxy(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address oldImplementationAddress,
                        address indexed newImplementationAddress
                      );
                      /**
                       * @notice Returns the id of the Aave market to which this contract points to.
                       * @return The market id
                       */
                      function getMarketId() external view returns (string memory);
                      /**
                       * @notice Associates an id with a specific PoolAddressesProvider.
                       * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                       * identify and validate multiple Aave markets.
                       * @param newMarketId The market id
                       */
                      function setMarketId(string calldata newMarketId) external;
                      /**
                       * @notice Returns an address by its identifier.
                       * @dev The returned address might be an EOA or a contract, potentially proxied
                       * @dev It returns ZERO if there is no registered address with the given id
                       * @param id The id
                       * @return The address of the registered for the specified id
                       */
                      function getAddress(bytes32 id) external view returns (address);
                      /**
                       * @notice 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 `newImplementationAddress`.
                       * @dev 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 newImplementationAddress The address of the new implementation
                       */
                      function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                      /**
                       * @notice Sets an address for an id replacing the address saved in the addresses map.
                       * @dev 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;
                      /**
                       * @notice Returns the address of the Pool proxy.
                       * @return The Pool proxy address
                       */
                      function getPool() external view returns (address);
                      /**
                       * @notice Updates the implementation of the Pool, or creates a proxy
                       * setting the new `pool` implementation when the function is called for the first time.
                       * @param newPoolImpl The new Pool implementation
                       */
                      function setPoolImpl(address newPoolImpl) external;
                      /**
                       * @notice Returns the address of the PoolConfigurator proxy.
                       * @return The PoolConfigurator proxy address
                       */
                      function getPoolConfigurator() external view returns (address);
                      /**
                       * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                       * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                       * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                       */
                      function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                      /**
                       * @notice Returns the address of the price oracle.
                       * @return The address of the PriceOracle
                       */
                      function getPriceOracle() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle.
                       * @param newPriceOracle The address of the new PriceOracle
                       */
                      function setPriceOracle(address newPriceOracle) external;
                      /**
                       * @notice Returns the address of the ACL manager.
                       * @return The address of the ACLManager
                       */
                      function getACLManager() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL manager.
                       * @param newAclManager The address of the new ACLManager
                       */
                      function setACLManager(address newAclManager) external;
                      /**
                       * @notice Returns the address of the ACL admin.
                       * @return The address of the ACL admin
                       */
                      function getACLAdmin() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL admin.
                       * @param newAclAdmin The address of the new ACL admin
                       */
                      function setACLAdmin(address newAclAdmin) external;
                      /**
                       * @notice Returns the address of the price oracle sentinel.
                       * @return The address of the PriceOracleSentinel
                       */
                      function getPriceOracleSentinel() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle sentinel.
                       * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                       */
                      function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                      /**
                       * @notice Returns the address of the data provider.
                       * @return The address of the DataProvider
                       */
                      function getPoolDataProvider() external view returns (address);
                      /**
                       * @notice Updates the address of the data provider.
                       * @param newDataProvider The address of the new DataProvider
                       */
                      function setPoolDataProvider(address newDataProvider) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.10;
                    /**
                     * @dev External interface of AccessControl declared to support ERC165 detection.
                     */
                    interface IAccessControl {
                      /**
                       * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                       *
                       * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                       * {RoleAdminChanged} not being emitted signaling this.
                       *
                       * _Available since v3.1._
                       */
                      event RoleAdminChanged(
                        bytes32 indexed role,
                        bytes32 indexed previousAdminRole,
                        bytes32 indexed newAdminRole
                      );
                      /**
                       * @dev Emitted when `account` is granted `role`.
                       *
                       * `sender` is the account that originated the contract call, an admin role
                       * bearer except when using {AccessControl-_setupRole}.
                       */
                      event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Emitted when `account` is revoked `role`.
                       *
                       * `sender` is the account that originated the contract call:
                       *   - if using `revokeRole`, it is the admin role bearer
                       *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                       */
                      event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                      /**
                       * @dev Returns `true` if `account` has been granted `role`.
                       */
                      function hasRole(bytes32 role, address account) external view returns (bool);
                      /**
                       * @dev Returns the admin role that controls `role`. See {grantRole} and
                       * {revokeRole}.
                       *
                       * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                       */
                      function getRoleAdmin(bytes32 role) external view returns (bytes32);
                      /**
                       * @dev Grants `role` to `account`.
                       *
                       * If `account` had not been already granted `role`, emits a {RoleGranted}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function grantRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from `account`.
                       *
                       * If `account` had been granted `role`, emits a {RoleRevoked} event.
                       *
                       * Requirements:
                       *
                       * - the caller must have ``role``'s admin role.
                       */
                      function revokeRole(bytes32 role, address account) external;
                      /**
                       * @dev Revokes `role` from the calling account.
                       *
                       * Roles are often managed via {grantRole} and {revokeRole}: this function's
                       * purpose is to provide a mechanism for accounts to lose their privileges
                       * if they are compromised (such as when a trusted device is misplaced).
                       *
                       * If the calling account had been granted `role`, emits a {RoleRevoked}
                       * event.
                       *
                       * Requirements:
                       *
                       * - the caller must be `account`.
                       */
                      function renounceRole(bytes32 role, address account) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {Errors} from '../helpers/Errors.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveConfiguration} from './ReserveConfiguration.sol';
                    /**
                     * @title EModeConfiguration library
                     * @author BGD Labs
                     * @notice Implements the bitmap logic to handle the eMode configuration
                     */
                    library EModeConfiguration {
                      /**
                       * @notice Sets a bit in a given bitmap that represents the reserve index range
                       * @dev The supplied bitmap is supposed to be a uint128 in which each bit represents a reserve
                       * @param bitmap The bitmap
                       * @param reserveIndex The index of the reserve in the bitmap
                       * @param enabled True if the reserveIndex should be enabled on the bitmap, false otherwise
                       * @return The altered bitmap
                       */
                      function setReserveBitmapBit(
                        uint128 bitmap,
                        uint256 reserveIndex,
                        bool enabled
                      ) internal pure returns (uint128) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          uint128 bit = uint128(1 << reserveIndex);
                          if (enabled) {
                            return bitmap | bit;
                          } else {
                            return bitmap & ~bit;
                          }
                        }
                      }
                      /**
                       * @notice Validates if a reserveIndex is flagged as enabled on a given bitmap
                       * @param bitmap The bitmap
                       * @param reserveIndex The index of the reserve in the bitmap
                       * @return True if the reserveindex is flagged true
                       */
                      function isReserveEnabledOnBitmap(
                        uint128 bitmap,
                        uint256 reserveIndex
                      ) internal pure returns (bool) {
                        unchecked {
                          require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX);
                          return (bitmap >> reserveIndex) & 1 != 0;
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                    import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                    import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {EModeConfiguration} from '../configuration/EModeConfiguration.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    import {EModeLogic} from './EModeLogic.sol';
                    /**
                     * @title GenericLogic library
                     * @author Aave
                     * @notice Implements protocol-level logic to calculate and validate the state of a user
                     */
                    library GenericLogic {
                      using ReserveLogic for DataTypes.ReserveData;
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      struct CalculateUserAccountDataVars {
                        uint256 assetPrice;
                        uint256 assetUnit;
                        uint256 userBalanceInBaseCurrency;
                        uint256 decimals;
                        uint256 ltv;
                        uint256 liquidationThreshold;
                        uint256 i;
                        uint256 healthFactor;
                        uint256 totalCollateralInBaseCurrency;
                        uint256 totalDebtInBaseCurrency;
                        uint256 avgLtv;
                        uint256 avgLiquidationThreshold;
                        uint256 eModeLtv;
                        uint256 eModeLiqThreshold;
                        address currentReserveAddress;
                        bool hasZeroLtvCollateral;
                        bool isInEModeCategory;
                      }
                      /**
                       * @notice Calculates the user data across the reserves.
                       * @dev It includes the total liquidity/collateral/borrow balances in the base currency used by the price feed,
                       * the average Loan To Value, the average Liquidation Ratio, and the Health factor.
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param params Additional parameters needed for the calculation
                       * @return The total collateral of the user in the base currency used by the price feed
                       * @return The total debt of the user in the base currency used by the price feed
                       * @return The average ltv of the user
                       * @return The average liquidation threshold of the user
                       * @return The health factor of the user
                       * @return True if the ltv is zero, false otherwise
                       */
                      function calculateUserAccountData(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        DataTypes.CalculateUserAccountDataParams memory params
                      ) internal view returns (uint256, uint256, uint256, uint256, uint256, bool) {
                        if (params.userConfig.isEmpty()) {
                          return (0, 0, 0, 0, type(uint256).max, false);
                        }
                        CalculateUserAccountDataVars memory vars;
                        if (params.userEModeCategory != 0) {
                          vars.eModeLtv = eModeCategories[params.userEModeCategory].ltv;
                          vars.eModeLiqThreshold = eModeCategories[params.userEModeCategory].liquidationThreshold;
                        }
                        while (vars.i < params.reservesCount) {
                          if (!params.userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
                            unchecked {
                              ++vars.i;
                            }
                            continue;
                          }
                          vars.currentReserveAddress = reservesList[vars.i];
                          if (vars.currentReserveAddress == address(0)) {
                            unchecked {
                              ++vars.i;
                            }
                            continue;
                          }
                          DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
                          (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve
                            .configuration
                            .getParams();
                          unchecked {
                            vars.assetUnit = 10 ** vars.decimals;
                          }
                          vars.assetPrice = IPriceOracleGetter(params.oracle).getAssetPrice(vars.currentReserveAddress);
                          if (vars.liquidationThreshold != 0 && params.userConfig.isUsingAsCollateral(vars.i)) {
                            vars.userBalanceInBaseCurrency = _getUserBalanceInBaseCurrency(
                              params.user,
                              currentReserve,
                              vars.assetPrice,
                              vars.assetUnit
                            );
                            vars.totalCollateralInBaseCurrency += vars.userBalanceInBaseCurrency;
                            vars.isInEModeCategory =
                              params.userEModeCategory != 0 &&
                              EModeConfiguration.isReserveEnabledOnBitmap(
                                eModeCategories[params.userEModeCategory].collateralBitmap,
                                vars.i
                              );
                            if (vars.ltv != 0) {
                              vars.avgLtv +=
                                vars.userBalanceInBaseCurrency *
                                (vars.isInEModeCategory ? vars.eModeLtv : vars.ltv);
                            } else {
                              vars.hasZeroLtvCollateral = true;
                            }
                            vars.avgLiquidationThreshold +=
                              vars.userBalanceInBaseCurrency *
                              (vars.isInEModeCategory ? vars.eModeLiqThreshold : vars.liquidationThreshold);
                          }
                          if (params.userConfig.isBorrowing(vars.i)) {
                            vars.totalDebtInBaseCurrency += _getUserDebtInBaseCurrency(
                              params.user,
                              currentReserve,
                              vars.assetPrice,
                              vars.assetUnit
                            );
                          }
                          unchecked {
                            ++vars.i;
                          }
                        }
                        unchecked {
                          vars.avgLtv = vars.totalCollateralInBaseCurrency != 0
                            ? vars.avgLtv / vars.totalCollateralInBaseCurrency
                            : 0;
                          vars.avgLiquidationThreshold = vars.totalCollateralInBaseCurrency != 0
                            ? vars.avgLiquidationThreshold / vars.totalCollateralInBaseCurrency
                            : 0;
                        }
                        vars.healthFactor = (vars.totalDebtInBaseCurrency == 0)
                          ? type(uint256).max
                          : (vars.totalCollateralInBaseCurrency.percentMul(vars.avgLiquidationThreshold)).wadDiv(
                            vars.totalDebtInBaseCurrency
                          );
                        return (
                          vars.totalCollateralInBaseCurrency,
                          vars.totalDebtInBaseCurrency,
                          vars.avgLtv,
                          vars.avgLiquidationThreshold,
                          vars.healthFactor,
                          vars.hasZeroLtvCollateral
                        );
                      }
                      /**
                       * @notice Calculates the maximum amount that can be borrowed depending on the available collateral, the total debt
                       * and the average Loan To Value
                       * @param totalCollateralInBaseCurrency The total collateral in the base currency used by the price feed
                       * @param totalDebtInBaseCurrency The total borrow balance in the base currency used by the price feed
                       * @param ltv The average loan to value
                       * @return The amount available to borrow in the base currency of the used by the price feed
                       */
                      function calculateAvailableBorrows(
                        uint256 totalCollateralInBaseCurrency,
                        uint256 totalDebtInBaseCurrency,
                        uint256 ltv
                      ) internal pure returns (uint256) {
                        uint256 availableBorrowsInBaseCurrency = totalCollateralInBaseCurrency.percentMul(ltv);
                        if (availableBorrowsInBaseCurrency <= totalDebtInBaseCurrency) {
                          return 0;
                        }
                        availableBorrowsInBaseCurrency = availableBorrowsInBaseCurrency - totalDebtInBaseCurrency;
                        return availableBorrowsInBaseCurrency;
                      }
                      /**
                       * @notice Calculates total debt of the user in the based currency used to normalize the values of the assets
                       * @dev This fetches the `balanceOf` of the variable debt token for the user. For gas reasons, the
                       * variable debt balance is calculated by fetching `scaledBalancesOf` normalized debt, which is cheaper than
                       * fetching `balanceOf`
                       * @param user The address of the user
                       * @param reserve The data of the reserve for which the total debt of the user is being calculated
                       * @param assetPrice The price of the asset for which the total debt of the user is being calculated
                       * @param assetUnit The value representing one full unit of the asset (10^decimals)
                       * @return The total debt of the user normalized to the base currency
                       */
                      function _getUserDebtInBaseCurrency(
                        address user,
                        DataTypes.ReserveData storage reserve,
                        uint256 assetPrice,
                        uint256 assetUnit
                      ) private view returns (uint256) {
                        // fetching variable debt
                        uint256 userTotalDebt = IScaledBalanceToken(reserve.variableDebtTokenAddress).scaledBalanceOf(
                          user
                        );
                        if (userTotalDebt == 0) {
                          return 0;
                        }
                        userTotalDebt = userTotalDebt.rayMul(reserve.getNormalizedDebt()) * assetPrice;
                        unchecked {
                          return userTotalDebt / assetUnit;
                        }
                      }
                      /**
                       * @notice Calculates total aToken balance of the user in the based currency used by the price oracle
                       * @dev For gas reasons, the aToken balance is calculated by fetching `scaledBalancesOf` normalized debt, which
                       * is cheaper than fetching `balanceOf`
                       * @param user The address of the user
                       * @param reserve The data of the reserve for which the total aToken balance of the user is being calculated
                       * @param assetPrice The price of the asset for which the total aToken balance of the user is being calculated
                       * @param assetUnit The value representing one full unit of the asset (10^decimals)
                       * @return The total aToken balance of the user normalized to the base currency of the price oracle
                       */
                      function _getUserBalanceInBaseCurrency(
                        address user,
                        DataTypes.ReserveData storage reserve,
                        uint256 assetPrice,
                        uint256 assetUnit
                      ) private view returns (uint256) {
                        uint256 normalizedIncome = reserve.getNormalizedIncome();
                        uint256 balance = (
                          IScaledBalanceToken(reserve.aTokenAddress).scaledBalanceOf(user).rayMul(normalizedIncome)
                        ) * assetPrice;
                        unchecked {
                          return balance / assetUnit;
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                    pragma solidity ^0.8.10;
                    /**
                     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                     * checks.
                     *
                     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                     * easily result in undesired exploitation or bugs, since developers usually
                     * assume that overflows raise errors. `SafeCast` restores this intuition by
                     * reverting the transaction when such 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.
                     *
                     * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                     * all math on `uint256` and `int256` and then downcasting.
                     */
                    library SafeCast {
                      /**
                       * @dev Returns the downcasted uint224 from uint256, reverting on
                       * overflow (when the input is greater than largest uint224).
                       *
                       * Counterpart to Solidity's `uint224` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 224 bits
                       */
                      function toUint224(uint256 value) internal pure returns (uint224) {
                        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                        return uint224(value);
                      }
                      /**
                       * @dev Returns the downcasted uint128 from uint256, reverting on
                       * overflow (when the input is greater than largest uint128).
                       *
                       * Counterpart to Solidity's `uint128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       */
                      function toUint128(uint256 value) internal pure returns (uint128) {
                        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                        return uint128(value);
                      }
                      /**
                       * @dev Returns the downcasted uint96 from uint256, reverting on
                       * overflow (when the input is greater than largest uint96).
                       *
                       * Counterpart to Solidity's `uint96` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 96 bits
                       */
                      function toUint96(uint256 value) internal pure returns (uint96) {
                        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                        return uint96(value);
                      }
                      /**
                       * @dev Returns the downcasted uint64 from uint256, reverting on
                       * overflow (when the input is greater than largest uint64).
                       *
                       * Counterpart to Solidity's `uint64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       */
                      function toUint64(uint256 value) internal pure returns (uint64) {
                        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                        return uint64(value);
                      }
                      /**
                       * @dev Returns the downcasted uint32 from uint256, reverting on
                       * overflow (when the input is greater than largest uint32).
                       *
                       * Counterpart to Solidity's `uint32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       */
                      function toUint32(uint256 value) internal pure returns (uint32) {
                        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                        return uint32(value);
                      }
                      /**
                       * @dev Returns the downcasted uint16 from uint256, reverting on
                       * overflow (when the input is greater than largest uint16).
                       *
                       * Counterpart to Solidity's `uint16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       */
                      function toUint16(uint256 value) internal pure returns (uint16) {
                        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                        return uint16(value);
                      }
                      /**
                       * @dev Returns the downcasted uint8 from uint256, reverting on
                       * overflow (when the input is greater than largest uint8).
                       *
                       * Counterpart to Solidity's `uint8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       */
                      function toUint8(uint256 value) internal pure returns (uint8) {
                        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                        return uint8(value);
                      }
                      /**
                       * @dev Converts a signed int256 into an unsigned uint256.
                       *
                       * Requirements:
                       *
                       * - input must be greater than or equal to 0.
                       */
                      function toUint256(int256 value) internal pure returns (uint256) {
                        require(value >= 0, 'SafeCast: value must be positive');
                        return uint256(value);
                      }
                      /**
                       * @dev Returns the downcasted int128 from int256, reverting on
                       * overflow (when the input is less than smallest int128 or
                       * greater than largest int128).
                       *
                       * Counterpart to Solidity's `int128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt128(int256 value) internal pure returns (int128) {
                        require(
                          value >= type(int128).min && value <= type(int128).max,
                          "SafeCast: value doesn't fit in 128 bits"
                        );
                        return int128(value);
                      }
                      /**
                       * @dev Returns the downcasted int64 from int256, reverting on
                       * overflow (when the input is less than smallest int64 or
                       * greater than largest int64).
                       *
                       * Counterpart to Solidity's `int64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt64(int256 value) internal pure returns (int64) {
                        require(
                          value >= type(int64).min && value <= type(int64).max,
                          "SafeCast: value doesn't fit in 64 bits"
                        );
                        return int64(value);
                      }
                      /**
                       * @dev Returns the downcasted int32 from int256, reverting on
                       * overflow (when the input is less than smallest int32 or
                       * greater than largest int32).
                       *
                       * Counterpart to Solidity's `int32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt32(int256 value) internal pure returns (int32) {
                        require(
                          value >= type(int32).min && value <= type(int32).max,
                          "SafeCast: value doesn't fit in 32 bits"
                        );
                        return int32(value);
                      }
                      /**
                       * @dev Returns the downcasted int16 from int256, reverting on
                       * overflow (when the input is less than smallest int16 or
                       * greater than largest int16).
                       *
                       * Counterpart to Solidity's `int16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt16(int256 value) internal pure returns (int16) {
                        require(
                          value >= type(int16).min && value <= type(int16).max,
                          "SafeCast: value doesn't fit in 16 bits"
                        );
                        return int16(value);
                      }
                      /**
                       * @dev Returns the downcasted int8 from int256, reverting on
                       * overflow (when the input is less than smallest int8 or
                       * greater than largest int8).
                       *
                       * Counterpart to Solidity's `int8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       *
                       * _Available since v3.1._
                       */
                      function toInt8(int256 value) internal pure returns (int8) {
                        require(
                          value >= type(int8).min && value <= type(int8).max,
                          "SafeCast: value doesn't fit in 8 bits"
                        );
                        return int8(value);
                      }
                      /**
                       * @dev Converts an unsigned uint256 into a signed int256.
                       *
                       * Requirements:
                       *
                       * - input must be less than or equal to maxInt256.
                       */
                      function toInt256(uint256 value) internal pure returns (int256) {
                        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                        return int256(value);
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.10;
                    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 {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                    import {Errors} from '../../libraries/helpers/Errors.sol';
                    import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IACLManager} from '../../../interfaces/IACLManager.sol';
                    /**
                     * @title IncentivizedERC20
                     * @author Aave, inspired by the Openzeppelin ERC20 implementation
                     * @notice Basic ERC20 implementation
                     */
                    abstract contract IncentivizedERC20 is Context, IERC20Detailed {
                      using WadRayMath for uint256;
                      using SafeCast for uint256;
                      /**
                       * @dev Only pool admin can call functions marked by this modifier.
                       */
                      modifier onlyPoolAdmin() {
                        IACLManager aclManager = IACLManager(_addressesProvider.getACLManager());
                        require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);
                        _;
                      }
                      /**
                       * @dev Only pool can call functions marked by this modifier.
                       */
                      modifier onlyPool() {
                        require(_msgSender() == address(POOL), Errors.CALLER_MUST_BE_POOL);
                        _;
                      }
                      /**
                       * @dev UserState - additionalData is a flexible field.
                       * ATokens and VariableDebtTokens use this field store the index of the
                       * user's last supply/withdrawal/borrow/repayment.
                       */
                      struct UserState {
                        uint128 balance;
                        uint128 additionalData;
                      }
                      // Map of users address and their state data (userAddress => userStateData)
                      mapping(address => UserState) internal _userState;
                      // Map of allowances (delegator => delegatee => allowanceAmount)
                      mapping(address => mapping(address => uint256)) private _allowances;
                      uint256 internal _totalSupply;
                      string private _name;
                      string private _symbol;
                      uint8 private _decimals;
                      IAaveIncentivesController internal _incentivesController;
                      IPoolAddressesProvider internal immutable _addressesProvider;
                      IPool public immutable POOL;
                      /**
                       * @dev Constructor.
                       * @param pool The reference to the main Pool contract
                       * @param name_ The name of the token
                       * @param symbol_ The symbol of the token
                       * @param decimals_ The number of decimals of the token
                       */
                      constructor(IPool pool, string memory name_, string memory symbol_, uint8 decimals_) {
                        _addressesProvider = pool.ADDRESSES_PROVIDER();
                        _name = name_;
                        _symbol = symbol_;
                        _decimals = decimals_;
                        POOL = pool;
                      }
                      /// @inheritdoc IERC20Detailed
                      function name() public view override returns (string memory) {
                        return _name;
                      }
                      /// @inheritdoc IERC20Detailed
                      function symbol() external view override returns (string memory) {
                        return _symbol;
                      }
                      /// @inheritdoc IERC20Detailed
                      function decimals() external view override returns (uint8) {
                        return _decimals;
                      }
                      /// @inheritdoc IERC20
                      function totalSupply() public view virtual override returns (uint256) {
                        return _totalSupply;
                      }
                      /// @inheritdoc IERC20
                      function balanceOf(address account) public view virtual override returns (uint256) {
                        return _userState[account].balance;
                      }
                      /**
                       * @notice Returns the address of the Incentives Controller contract
                       * @return The address of the Incentives Controller
                       */
                      function getIncentivesController() external view virtual returns (IAaveIncentivesController) {
                        return _incentivesController;
                      }
                      /**
                       * @notice Sets a new Incentives Controller
                       * @param controller the new Incentives controller
                       */
                      function setIncentivesController(IAaveIncentivesController controller) external onlyPoolAdmin {
                        _incentivesController = controller;
                      }
                      /// @inheritdoc IERC20
                      function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
                        uint128 castAmount = amount.toUint128();
                        _transfer(_msgSender(), recipient, castAmount);
                        return true;
                      }
                      /// @inheritdoc IERC20
                      function allowance(
                        address owner,
                        address spender
                      ) external view virtual override returns (uint256) {
                        return _allowances[owner][spender];
                      }
                      /// @inheritdoc IERC20
                      function approve(address spender, uint256 amount) external virtual override returns (bool) {
                        _approve(_msgSender(), spender, amount);
                        return true;
                      }
                      /// @inheritdoc IERC20
                      function transferFrom(
                        address sender,
                        address recipient,
                        uint256 amount
                      ) external virtual override returns (bool) {
                        uint128 castAmount = amount.toUint128();
                        _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - castAmount);
                        _transfer(sender, recipient, castAmount);
                        return true;
                      }
                      /**
                       * @notice 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) external virtual returns (bool) {
                        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                        return true;
                      }
                      /**
                       * @notice 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
                      ) external virtual returns (bool) {
                        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
                        return true;
                      }
                      /**
                       * @notice Transfers tokens between two users and apply incentives if defined.
                       * @param sender The source address
                       * @param recipient The destination address
                       * @param amount The amount getting transferred
                       */
                      function _transfer(address sender, address recipient, uint128 amount) internal virtual {
                        uint128 oldSenderBalance = _userState[sender].balance;
                        _userState[sender].balance = oldSenderBalance - amount;
                        uint128 oldRecipientBalance = _userState[recipient].balance;
                        _userState[recipient].balance = oldRecipientBalance + amount;
                        IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                        if (address(incentivesControllerLocal) != address(0)) {
                          uint256 currentTotalSupply = _totalSupply;
                          incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);
                          if (sender != recipient) {
                            incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                          }
                        }
                      }
                      /**
                       * @notice Approve `spender` to use `amount` of `owner`s balance
                       * @param owner The address owning the tokens
                       * @param spender The address approved for spending
                       * @param amount The amount of tokens to approve spending of
                       */
                      function _approve(address owner, address spender, uint256 amount) internal virtual {
                        _allowances[owner][spender] = amount;
                        emit Approval(owner, spender, amount);
                      }
                      /**
                       * @notice Update the name of the token
                       * @param newName The new name for the token
                       */
                      function _setName(string memory newName) internal {
                        _name = newName;
                      }
                      /**
                       * @notice Update the symbol for the token
                       * @param newSymbol The new symbol for the token
                       */
                      function _setSymbol(string memory newSymbol) internal {
                        _symbol = newSymbol;
                      }
                      /**
                       * @notice Update the number of decimals for the token
                       * @param newDecimals The new number of decimals for the token
                       */
                      function _setDecimals(uint8 newDecimals) internal {
                        _decimals = newDecimals;
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                    import {IInitializableDebtToken} from './IInitializableDebtToken.sol';
                    /**
                     * @title IVariableDebtToken
                     * @author Aave
                     * @notice Defines the basic interface for a variable debt token.
                     */
                    interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken {
                      /**
                       * @notice 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 previous balance of the user is 0, false otherwise
                       * @return The scaled total debt of the reserve
                       */
                      function mint(
                        address user,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) external returns (bool, uint256);
                      /**
                       * @notice Burns user variable debt
                       * @dev In some instances, a burn transaction will emit a mint event
                       * if the amount to burn is less than the interest that the user accrued
                       * @param from The address from which the debt will be burned
                       * @param amount The amount getting burned
                       * @param index The variable debt index of the reserve
                       * @return The scaled total debt of the reserve
                       */
                      function burn(address from, uint256 amount, uint256 index) external returns (uint256);
                      /**
                       * @notice Returns the address of the underlying asset of this debtToken (E.g. WETH for variableDebtWETH)
                       * @return The address of the underlying asset
                       */
                      function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    import {WadRayMath} from './WadRayMath.sol';
                    /**
                     * @title MathUtils library
                     * @author Aave
                     * @notice Provides functions to perform linear and compounded interest calculations
                     */
                    library MathUtils {
                      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 result = rate * (block.timestamp - uint256(lastUpdateTimestamp));
                        unchecked {
                          result = result / SECONDS_PER_YEAR;
                        }
                        return WadRayMath.RAY + result;
                      }
                      /**
                       * @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 - uint256(lastUpdateTimestamp);
                        if (exp == 0) {
                          return WadRayMath.RAY;
                        }
                        uint256 expMinusOne;
                        uint256 expMinusTwo;
                        uint256 basePowerTwo;
                        uint256 basePowerThree;
                        unchecked {
                          expMinusOne = exp - 1;
                          expMinusTwo = exp > 2 ? exp - 2 : 0;
                          basePowerTwo = rate.rayMul(rate) / (SECONDS_PER_YEAR * SECONDS_PER_YEAR);
                          basePowerThree = basePowerTwo.rayMul(rate) / SECONDS_PER_YEAR;
                        }
                        uint256 secondTerm = exp * expMinusOne * basePowerTwo;
                        unchecked {
                          secondTerm /= 2;
                        }
                        uint256 thirdTerm = exp * expMinusOne * expMinusTwo * basePowerThree;
                        unchecked {
                          thirdTerm /= 6;
                        }
                        return WadRayMath.RAY + (rate * exp) / SECONDS_PER_YEAR + secondTerm + 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
                       * @return The interest rate compounded between lastUpdateTimestamp and current block timestamp, in ray
                       */
                      function calculateCompoundedInterest(
                        uint256 rate,
                        uint40 lastUpdateTimestamp
                      ) internal view returns (uint256) {
                        return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp);
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IAaveIncentivesController
                     * @author Aave
                     * @notice Defines the basic interface for an Aave Incentives Controller.
                     * @dev It only contains one single function, needed as a hook on aToken and debtToken transfers.
                     */
                    interface IAaveIncentivesController {
                      /**
                       * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
                       * @dev The units of `totalSupply` and `userBalance` should be the same.
                       * @param user The address of the user whose asset balance has changed
                       * @param totalSupply The total supply of the asset prior to user balance change
                       * @param userBalance The previous user balance prior to balance change
                       */
                      function handleAction(address user, uint256 totalSupply, uint256 userBalance) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                    /**
                     * @title IPool
                     * @author Aave
                     * @notice Defines the basic interface for an Aave Pool.
                     */
                    interface IPool {
                      /**
                       * @dev Emitted on mintUnbacked()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param user The address initiating the supply
                       * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens
                       * @param amount The amount of supplied assets
                       * @param referralCode The referral code used
                       */
                      event MintUnbacked(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted on backUnbacked()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param backer The address paying for the backing
                       * @param amount The amount added as backing
                       * @param fee The amount paid in fees
                       */
                      event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                      /**
                       * @dev Emitted on supply()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param user The address initiating the supply
                       * @param onBehalfOf The beneficiary of the supply, receiving the aTokens
                       * @param amount The amount supplied
                       * @param referralCode The referral code used
                       */
                      event Supply(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted on withdraw()
                       * @param reserve The address of the underlying asset being withdrawn
                       * @param user The address initiating the withdrawal, owner of aTokens
                       * @param to The 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 interestRateMode The rate mode: 2 for Variable, 1 is deprecated (changed on v3.2.0)
                       * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray
                       * @param referralCode The referral code used
                       */
                      event Borrow(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 borrowRate,
                        uint16 indexed referralCode
                      );
                      /**
                       * @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
                       * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
                       */
                      event Repay(
                        address indexed reserve,
                        address indexed user,
                        address indexed repayer,
                        uint256 amount,
                        bool useATokens
                      );
                      /**
                       * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets
                       * @param asset The address of the underlying asset of the reserve
                       * @param totalDebt The total isolation mode debt for the reserve
                       */
                      event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                      /**
                       * @dev Emitted when the user selects a certain asset category for eMode
                       * @param user The address of the user
                       * @param categoryId The category id
                       */
                      event UserEModeSet(address indexed user, uint8 categoryId);
                      /**
                       * @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 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 interestRateMode The flashloan mode: 0 for regular flashloan,
                       *        1 for Stable (Deprecated on v3.2.0), 2 for Variable
                       * @param premium The fee flash borrowed
                       * @param referralCode The referral code used
                       */
                      event FlashLoan(
                        address indexed target,
                        address initiator,
                        address indexed asset,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 premium,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted when a borrower is liquidated.
                       * @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 liquidator
                       * @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.
                       * @param reserve The address of the underlying asset of the reserve
                       * @param liquidityRate The next liquidity rate
                       * @param stableBorrowRate The next stable borrow rate @note deprecated on v3.2.0
                       * @param variableBorrowRate The next variable borrow rate
                       * @param liquidityIndex The next liquidity index
                       * @param variableBorrowIndex The next variable borrow index
                       */
                      event ReserveDataUpdated(
                        address indexed reserve,
                        uint256 liquidityRate,
                        uint256 stableBorrowRate,
                        uint256 variableBorrowRate,
                        uint256 liquidityIndex,
                        uint256 variableBorrowIndex
                      );
                      /**
                       * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.
                       * @param reserve The address of the reserve
                       * @param amountMinted The amount minted to the treasury
                       */
                      event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                      /**
                       * @notice Mints an `amount` of aTokens to the `onBehalfOf`
                       * @param asset The address of the underlying asset to mint
                       * @param amount The amount to mint
                       * @param onBehalfOf The address that will receive the aTokens
                       * @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 mintUnbacked(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Back the current unbacked underlying with `amount` and pay `fee`.
                       * @param asset The address of the underlying asset to back
                       * @param amount The amount to back
                       * @param fee The amount paid in fees
                       * @return The backed amount
                       */
                      function backUnbacked(address asset, uint256 amount, uint256 fee) external returns (uint256);
                      /**
                       * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                       * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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 supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
                      /**
                       * @notice Supply with transfer approval of asset to be supplied done via permit function
                       * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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 deadline The deadline timestamp that the permit is valid
                       * @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 permitV The V parameter of ERC712 permit sig
                       * @param permitR The R parameter of ERC712 permit sig
                       * @param permitS The S parameter of ERC712 permit sig
                       */
                      function supplyWithPermit(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) external;
                      /**
                       * @notice 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 The 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);
                      /**
                       * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                       * already supplied enough collateral, or he was given enough allowance by a credit delegator on the VariableDebtToken
                       * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                       *   and 100 variable debt tokens
                       * @param asset The address of the underlying asset to borrow
                       * @param amount The amount to be borrowed
                       * @param interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                       * @param referralCode The 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 The 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 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 interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                       * @param onBehalfOf The 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 interestRateMode,
                        address onBehalfOf
                      ) external returns (uint256);
                      /**
                       * @notice Repay with transfer approval of asset to be repaid done via permit function
                       * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                       * @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 interestRateMode 2 for Variable, 1 is deprecated on v3.2.0
                       * @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
                       * @param deadline The deadline timestamp that the permit is valid
                       * @param permitV The V parameter of ERC712 permit sig
                       * @param permitR The R parameter of ERC712 permit sig
                       * @param permitS The S parameter of ERC712 permit sig
                       * @return The final amount repaid
                       */
                      function repayWithPermit(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode,
                        address onBehalfOf,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) external returns (uint256);
                      /**
                       * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
                       * equivalent debt tokens
                       * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable debt tokens
                       * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
                       * balance is not enough to cover the whole debt
                       * @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 interestRateMode DEPRECATED in v3.2.0
                       * @return The final amount repaid
                       */
                      function repayWithATokens(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode
                      ) external returns (uint256);
                      /**
                       * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
                       * @param asset The address of the underlying asset supplied
                       * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
                       */
                      function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                      /**
                       * @notice 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;
                      /**
                       * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                       * as long as the amount taken plus a fee is returned.
                       * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                       * into consideration. For further details please visit https://docs.aave.com/developers/
                       * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanReceiver interface
                       * @param assets The addresses of the assets being flash-borrowed
                       * @param amounts The amounts of the assets being flash-borrowed
                       * @param interestRateModes 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 -> Deprecated on v3.2.0
                       *   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 2 on `modes`
                       * @param params Variadic packed params to pass to the receiver as extra information
                       * @param referralCode The 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 interestRateModes,
                        address onBehalfOf,
                        bytes calldata params,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                       * as long as the amount taken plus a fee is returned.
                       * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
                       * into consideration. For further details please visit https://docs.aave.com/developers/
                       * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface
                       * @param asset The address of the asset being flash-borrowed
                       * @param amount The amount of the asset being flash-borrowed
                       * @param params Variadic packed params to pass to the receiver as extra information
                       * @param referralCode The 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 flashLoanSimple(
                        address receiverAddress,
                        address asset,
                        uint256 amount,
                        bytes calldata params,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Returns the user account data across all the reserves
                       * @param user The address of the user
                       * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                       * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                       * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                       * @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 totalCollateralBase,
                          uint256 totalDebtBase,
                          uint256 availableBorrowsBase,
                          uint256 currentLiquidationThreshold,
                          uint256 ltv,
                          uint256 healthFactor
                        );
                      /**
                       * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                       * interest rate strategy
                       * @dev Only callable by the PoolConfigurator 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 variableDebtAddress 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 variableDebtAddress,
                        address interestRateStrategyAddress
                      ) external;
                      /**
                       * @notice Drop a reserve
                       * @dev Only callable by the PoolConfigurator contract
                       * @dev Does not reset eMode flags, which must be considered when reusing the same reserve id for a different reserve.
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function dropReserve(address asset) external;
                      /**
                       * @notice Updates the address of the interest rate strategy contract
                       * @dev Only callable by the PoolConfigurator 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;
                      /**
                       * @notice Accumulates interest to all indexes of the reserve
                       * @dev Only callable by the PoolConfigurator contract
                       * @dev To be used when required by the configurator, for example when updating interest rates strategy data
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function syncIndexesState(address asset) external;
                      /**
                       * @notice Updates interest rates on the reserve data
                       * @dev Only callable by the PoolConfigurator contract
                       * @dev To be used when required by the configurator, for example when updating interest rates strategy data
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function syncRatesState(address asset) external;
                      /**
                       * @notice Sets the configuration bitmap of the reserve as a whole
                       * @dev Only callable by the PoolConfigurator contract
                       * @param asset The address of the underlying asset of the reserve
                       * @param configuration The new configuration bitmap
                       */
                      function setConfiguration(
                        address asset,
                        DataTypes.ReserveConfigurationMap calldata configuration
                      ) external;
                      /**
                       * @notice 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);
                      /**
                       * @notice 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);
                      /**
                       * @notice Returns the 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);
                      /**
                       * @notice Returns the normalized variable debt per unit of asset
                       * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a
                       * "dynamic" variable index based on time, current stored index and virtual rate at the current
                       * moment (approx. a borrower would get if opening a position). This means that is always used in
                       * combination with variable debt supply/balances.
                       * If using this function externally, consider that is possible to have an increasing normalized
                       * variable debt that is not equivalent to how the variable debt index would be updated in storage
                       * (e.g. only updates with non-zero variable debt supply)
                       * @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);
                      /**
                       * @notice Returns the state and configuration of the reserve
                       * @param asset The address of the underlying asset of the reserve
                       * @return The state and configuration data of the reserve
                       */
                      function getReserveData(address asset) external view returns (DataTypes.ReserveDataLegacy memory);
                      /**
                       * @notice Returns the state and configuration of the reserve, including extra data included with Aave v3.1
                       * @param asset The address of the underlying asset of the reserve
                       * @return The state and configuration data of the reserve with virtual accounting
                       */
                      function getReserveDataExtended(
                        address asset
                      ) external view returns (DataTypes.ReserveData memory);
                      /**
                       * @notice Returns the virtual underlying balance of the reserve
                       * @param asset The address of the underlying asset of the reserve
                       * @return The reserve virtual underlying balance
                       */
                      function getVirtualUnderlyingBalance(address asset) external view returns (uint128);
                      /**
                       * @notice Validates and finalizes an aToken transfer
                       * @dev 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;
                      /**
                       * @notice Returns the list of the underlying assets of all the initialized reserves
                       * @dev It does not include dropped reserves
                       * @return The addresses of the underlying assets of the initialized reserves
                       */
                      function getReservesList() external view returns (address[] memory);
                      /**
                       * @notice Returns the number of initialized reserves
                       * @dev It includes dropped reserves
                       * @return The count
                       */
                      function getReservesCount() external view returns (uint256);
                      /**
                       * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct
                       * @param id The id of the reserve as stored in the DataTypes.ReserveData struct
                       * @return The address of the reserve associated with id
                       */
                      function getReserveAddressById(uint16 id) external view returns (address);
                      /**
                       * @notice Returns the PoolAddressesProvider connected to this contract
                       * @return The address of the PoolAddressesProvider
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Updates the protocol fee on the bridging
                       * @param bridgeProtocolFee The part of the premium sent to the protocol treasury
                       */
                      function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;
                      /**
                       * @notice Updates flash loan premiums. Flash loan premium consists of two parts:
                       * - A part is sent to aToken holders as extra, one time accumulated interest
                       * - A part is collected by the protocol treasury
                       * @dev The total premium is calculated on the total borrowed amount
                       * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`
                       * @dev Only callable by the PoolConfigurator contract
                       * @param flashLoanPremiumTotal The total premium, expressed in bps
                       * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps
                       */
                      function updateFlashloanPremiums(
                        uint128 flashLoanPremiumTotal,
                        uint128 flashLoanPremiumToProtocol
                      ) external;
                      /**
                       * @notice Configures a new or alters an existing collateral configuration of an eMode.
                       * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.
                       * The category 0 is reserved as it's the default for volatile assets
                       * @param id The id of the category
                       * @param config The configuration of the category
                       */
                      function configureEModeCategory(
                        uint8 id,
                        DataTypes.EModeCategoryBaseConfiguration memory config
                      ) external;
                      /**
                       * @notice Replaces the current eMode collateralBitmap.
                       * @param id The id of the category
                       * @param collateralBitmap The collateralBitmap of the category
                       */
                      function configureEModeCategoryCollateralBitmap(uint8 id, uint128 collateralBitmap) external;
                      /**
                       * @notice Replaces the current eMode borrowableBitmap.
                       * @param id The id of the category
                       * @param borrowableBitmap The borrowableBitmap of the category
                       */
                      function configureEModeCategoryBorrowableBitmap(uint8 id, uint128 borrowableBitmap) external;
                      /**
                       * @notice Returns the data of an eMode category
                       * @dev DEPRECATED use independent getters instead
                       * @param id The id of the category
                       * @return The configuration data of the category
                       */
                      function getEModeCategoryData(
                        uint8 id
                      ) external view returns (DataTypes.EModeCategoryLegacy memory);
                      /**
                       * @notice Returns the label of an eMode category
                       * @param id The id of the category
                       * @return The label of the category
                       */
                      function getEModeCategoryLabel(uint8 id) external view returns (string memory);
                      /**
                       * @notice Returns the collateral config of an eMode category
                       * @param id The id of the category
                       * @return The ltv,lt,lb of the category
                       */
                      function getEModeCategoryCollateralConfig(
                        uint8 id
                      ) external view returns (DataTypes.CollateralConfig memory);
                      /**
                       * @notice Returns the collateralBitmap of an eMode category
                       * @param id The id of the category
                       * @return The collateralBitmap of the category
                       */
                      function getEModeCategoryCollateralBitmap(uint8 id) external view returns (uint128);
                      /**
                       * @notice Returns the borrowableBitmap of an eMode category
                       * @param id The id of the category
                       * @return The borrowableBitmap of the category
                       */
                      function getEModeCategoryBorrowableBitmap(uint8 id) external view returns (uint128);
                      /**
                       * @notice Allows a user to use the protocol in eMode
                       * @param categoryId The id of the category
                       */
                      function setUserEMode(uint8 categoryId) external;
                      /**
                       * @notice Returns the eMode the user is using
                       * @param user The address of the user
                       * @return The eMode id
                       */
                      function getUserEMode(address user) external view returns (uint256);
                      /**
                       * @notice Resets the isolation mode total debt of the given asset to zero
                       * @dev It requires the given asset has zero debt ceiling
                       * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                       */
                      function resetIsolationModeTotalDebt(address asset) external;
                      /**
                       * @notice Sets the liquidation grace period of the given asset
                       * @dev To enable a liquidation grace period, a timestamp in the future should be set,
                       *      To disable a liquidation grace period, any timestamp in the past works, like 0
                       * @param asset The address of the underlying asset to set the liquidationGracePeriod
                       * @param until Timestamp when the liquidation grace period will end
                       **/
                      function setLiquidationGracePeriod(address asset, uint40 until) external;
                      /**
                       * @notice Returns the liquidation grace period of the given asset
                       * @param asset The address of the underlying asset
                       * @return Timestamp when the liquidation grace period will end
                       **/
                      function getLiquidationGracePeriod(address asset) external returns (uint40);
                      /**
                       * @notice Returns the total fee on flash loans
                       * @return The total fee on flashloans
                       */
                      function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);
                      /**
                       * @notice Returns the part of the bridge fees sent to protocol
                       * @return The bridge fee sent to the protocol treasury
                       */
                      function BRIDGE_PROTOCOL_FEE() external view returns (uint256);
                      /**
                       * @notice Returns the part of the flashloan fees sent to protocol
                       * @return The flashloan fee sent to the protocol treasury
                       */
                      function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);
                      /**
                       * @notice Returns the maximum number of reserves supported to be listed in this Pool
                       * @return The maximum number of reserves supported
                       */
                      function MAX_NUMBER_RESERVES() external view returns (uint16);
                      /**
                       * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                       * @param assets The list of reserves for which the minting needs to be executed
                       */
                      function mintToTreasury(address[] calldata assets) external;
                      /**
                       * @notice Rescue and transfer tokens locked in this contract
                       * @param token The address of the token
                       * @param to The address of the recipient
                       * @param amount The amount of token to transfer
                       */
                      function rescueTokens(address token, address to, uint256 amount) external;
                      /**
                       * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                       * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                       * @dev Deprecated: Use the `supply` function instead
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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;
                      /**
                       * @notice Gets the address of the external FlashLoanLogic
                       */
                      function getFlashLoanLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external BorrowLogic
                       */
                      function getBorrowLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external BridgeLogic
                       */
                      function getBridgeLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external EModeLogic
                       */
                      function getEModeLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external LiquidationLogic
                       */
                      function getLiquidationLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external PoolLogic
                       */
                      function getPoolLogic() external view returns (address);
                      /**
                       * @notice Gets the address of the external SupplyLogic
                       */
                      function getSupplyLogic() external view returns (address);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
                    import {UserConfiguration} from '../configuration/UserConfiguration.sol';
                    import {WadRayMath} from '../math/WadRayMath.sol';
                    import {PercentageMath} from '../math/PercentageMath.sol';
                    import {DataTypes} from '../types/DataTypes.sol';
                    import {ValidationLogic} from './ValidationLogic.sol';
                    import {ReserveLogic} from './ReserveLogic.sol';
                    /**
                     * @title EModeLogic library
                     * @author Aave
                     * @notice Implements the base logic for all the actions related to the eMode
                     */
                    library EModeLogic {
                      using ReserveLogic for DataTypes.ReserveCache;
                      using ReserveLogic for DataTypes.ReserveData;
                      using GPv2SafeERC20 for IERC20;
                      using UserConfiguration for DataTypes.UserConfigurationMap;
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      // See `IPool` for descriptions
                      event UserEModeSet(address indexed user, uint8 categoryId);
                      /**
                       * @notice Updates the user efficiency mode category
                       * @dev Will revert if user is borrowing non-compatible asset or change will drop HF < HEALTH_FACTOR_LIQUIDATION_THRESHOLD
                       * @dev Emits the `UserEModeSet` event
                       * @param reservesData The state of all the reserves
                       * @param reservesList The addresses of all the active reserves
                       * @param eModeCategories The configuration of all the efficiency mode categories
                       * @param usersEModeCategory The state of all users efficiency mode category
                       * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                       * @param params The additional parameters needed to execute the setUserEMode function
                       */
                      function executeSetUserEMode(
                        mapping(address => DataTypes.ReserveData) storage reservesData,
                        mapping(uint256 => address) storage reservesList,
                        mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                        mapping(address => uint8) storage usersEModeCategory,
                        DataTypes.UserConfigurationMap storage userConfig,
                        DataTypes.ExecuteSetUserEModeParams memory params
                      ) external {
                        if (usersEModeCategory[msg.sender] == params.categoryId) return;
                        ValidationLogic.validateSetUserEMode(
                          eModeCategories,
                          userConfig,
                          params.reservesCount,
                          params.categoryId
                        );
                        usersEModeCategory[msg.sender] = params.categoryId;
                        ValidationLogic.validateHealthFactor(
                          reservesData,
                          reservesList,
                          eModeCategories,
                          userConfig,
                          msg.sender,
                          params.categoryId,
                          params.reservesCount,
                          params.oracle
                        );
                        emit UserEModeSet(msg.sender, params.categoryId);
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.10;
                    /*
                     * @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 payable(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: MIT
                    pragma solidity ^0.8.10;
                    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: MIT
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    /**
                     * @title IACLManager
                     * @author Aave
                     * @notice Defines the basic interface for the ACL Manager
                     */
                    interface IACLManager {
                      /**
                       * @notice Returns the contract address of the PoolAddressesProvider
                       * @return The address of the PoolAddressesProvider
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Returns the identifier of the PoolAdmin role
                       * @return The id of the PoolAdmin role
                       */
                      function POOL_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the EmergencyAdmin role
                       * @return The id of the EmergencyAdmin role
                       */
                      function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the RiskAdmin role
                       * @return The id of the RiskAdmin role
                       */
                      function RISK_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the FlashBorrower role
                       * @return The id of the FlashBorrower role
                       */
                      function FLASH_BORROWER_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the Bridge role
                       * @return The id of the Bridge role
                       */
                      function BRIDGE_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the AssetListingAdmin role
                       * @return The id of the AssetListingAdmin role
                       */
                      function ASSET_LISTING_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Set the role as admin of a specific role.
                       * @dev By default the admin role for all roles is `DEFAULT_ADMIN_ROLE`.
                       * @param role The role to be managed by the admin role
                       * @param adminRole The admin role
                       */
                      function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
                      /**
                       * @notice Adds a new admin as PoolAdmin
                       * @param admin The address of the new admin
                       */
                      function addPoolAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as PoolAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removePoolAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is PoolAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is PoolAdmin, false otherwise
                       */
                      function isPoolAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new admin as EmergencyAdmin
                       * @param admin The address of the new admin
                       */
                      function addEmergencyAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as EmergencyAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeEmergencyAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is EmergencyAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is EmergencyAdmin, false otherwise
                       */
                      function isEmergencyAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new admin as RiskAdmin
                       * @param admin The address of the new admin
                       */
                      function addRiskAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as RiskAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeRiskAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is RiskAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is RiskAdmin, false otherwise
                       */
                      function isRiskAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new address as FlashBorrower
                       * @param borrower The address of the new FlashBorrower
                       */
                      function addFlashBorrower(address borrower) external;
                      /**
                       * @notice Removes an address as FlashBorrower
                       * @param borrower The address of the FlashBorrower to remove
                       */
                      function removeFlashBorrower(address borrower) external;
                      /**
                       * @notice Returns true if the address is FlashBorrower, false otherwise
                       * @param borrower The address to check
                       * @return True if the given address is FlashBorrower, false otherwise
                       */
                      function isFlashBorrower(address borrower) external view returns (bool);
                      /**
                       * @notice Adds a new address as Bridge
                       * @param bridge The address of the new Bridge
                       */
                      function addBridge(address bridge) external;
                      /**
                       * @notice Removes an address as Bridge
                       * @param bridge The address of the bridge to remove
                       */
                      function removeBridge(address bridge) external;
                      /**
                       * @notice Returns true if the address is Bridge, false otherwise
                       * @param bridge The address to check
                       * @return True if the given address is Bridge, false otherwise
                       */
                      function isBridge(address bridge) external view returns (bool);
                      /**
                       * @notice Adds a new admin as AssetListingAdmin
                       * @param admin The address of the new admin
                       */
                      function addAssetListingAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as AssetListingAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeAssetListingAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is AssetListingAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is AssetListingAdmin, false otherwise
                       */
                      function isAssetListingAdmin(address admin) external view returns (bool);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                    import {IPool} from './IPool.sol';
                    /**
                     * @title IInitializableDebtToken
                     * @author Aave
                     * @notice Interface for the initialize function common between debt tokens
                     */
                    interface IInitializableDebtToken {
                      /**
                       * @dev Emitted when a debt token is initialized
                       * @param underlyingAsset The address of the underlying asset
                       * @param pool The address of the associated 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
                      );
                      /**
                       * @notice Initializes the debt token.
                       * @param pool The pool contract that is initializing this contract
                       * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @param incentivesController The smart contract managing potential incentives distribution
                       * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's
                       * @param debtTokenName The name of the token
                       * @param debtTokenSymbol The symbol of the token
                       * @param params A set of encoded parameters for additional initialization
                       */
                      function initialize(
                        IPool pool,
                        address underlyingAsset,
                        IAaveIncentivesController incentivesController,
                        uint8 debtTokenDecimals,
                        string memory debtTokenName,
                        string memory debtTokenSymbol,
                        bytes calldata params
                      ) external;
                    }
                    

                    File 6 of 12: InitializableImmutableAdminUpgradeabilityProxy
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    /**
                     * @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.8.10;
                    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 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)
                        }
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    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.8.10;
                    /**
                     * @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.
                       * Will run if no other function in the contract matches the call data.
                       * 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());
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    import {BaseUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
                    /**
                     * @title BaseImmutableAdminUpgradeabilityProxy
                     * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
                     * @notice This contract combines an upgradeability proxy with an authorization
                     * mechanism for administrative tasks.
                     * @dev 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 internal immutable _admin;
                      /**
                       * @dev Constructor.
                       * @param admin The address of the admin
                       */
                      constructor(address admin) {
                        _admin = admin;
                      }
                      modifier ifAdmin() {
                        if (msg.sender == _admin) {
                          _;
                        } else {
                          _fallback();
                        }
                      }
                      /**
                       * @notice Return the admin address
                       * @return The address of the proxy admin.
                       */
                      function admin() external ifAdmin returns (address) {
                        return _admin;
                      }
                      /**
                       * @notice Return the implementation address
                       * @return The address of the implementation.
                       */
                      function implementation() external ifAdmin returns (address) {
                        return _implementation();
                      }
                      /**
                       * @notice Upgrade the backing implementation of the proxy.
                       * @dev Only the admin can call this function.
                       * @param newImplementation The address of the new implementation.
                       */
                      function upgradeTo(address newImplementation) external ifAdmin {
                        _upgradeTo(newImplementation);
                      }
                      /**
                       * @notice Upgrade the backing implementation of the proxy and call a function
                       * on the new implementation.
                       * @dev This is useful to initialize the proxied contract.
                       * @param newImplementation The 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);
                      }
                      /**
                       * @notice 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.8.10;
                    import {InitializableUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
                    import {Proxy} from '../../../dependencies/openzeppelin/upgradeability/Proxy.sol';
                    import {BaseImmutableAdminUpgradeabilityProxy} from './BaseImmutableAdminUpgradeabilityProxy.sol';
                    /**
                     * @title InitializableAdminUpgradeabilityProxy
                     * @author Aave
                     * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
                     */
                    contract InitializableImmutableAdminUpgradeabilityProxy is
                      BaseImmutableAdminUpgradeabilityProxy,
                      InitializableUpgradeabilityProxy
                    {
                      /**
                       * @dev Constructor.
                       * @param admin The address of the admin
                       */
                      constructor(address admin) BaseImmutableAdminUpgradeabilityProxy(admin) {
                        // Intentionally left blank
                      }
                      /// @inheritdoc BaseImmutableAdminUpgradeabilityProxy
                      function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                        BaseImmutableAdminUpgradeabilityProxy._willFallback();
                      }
                    }
                    

                    File 7 of 12: VariableDebtToken
                    // SPDX-License-Identifier: MIT
                    pragma solidity 0.8.10;
                    /*
                     * @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 payable(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.8.10;
                    /**
                     * @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.8.10;
                    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: MIT
                    // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                    pragma solidity 0.8.10;
                    /**
                     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                     * checks.
                     *
                     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                     * easily result in undesired exploitation or bugs, since developers usually
                     * assume that overflows raise errors. `SafeCast` restores this intuition by
                     * reverting the transaction when such 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.
                     *
                     * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                     * all math on `uint256` and `int256` and then downcasting.
                     */
                    library SafeCast {
                      /**
                       * @dev Returns the downcasted uint224 from uint256, reverting on
                       * overflow (when the input is greater than largest uint224).
                       *
                       * Counterpart to Solidity's `uint224` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 224 bits
                       */
                      function toUint224(uint256 value) internal pure returns (uint224) {
                        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                        return uint224(value);
                      }
                      /**
                       * @dev Returns the downcasted uint128 from uint256, reverting on
                       * overflow (when the input is greater than largest uint128).
                       *
                       * Counterpart to Solidity's `uint128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       */
                      function toUint128(uint256 value) internal pure returns (uint128) {
                        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                        return uint128(value);
                      }
                      /**
                       * @dev Returns the downcasted uint96 from uint256, reverting on
                       * overflow (when the input is greater than largest uint96).
                       *
                       * Counterpart to Solidity's `uint96` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 96 bits
                       */
                      function toUint96(uint256 value) internal pure returns (uint96) {
                        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                        return uint96(value);
                      }
                      /**
                       * @dev Returns the downcasted uint64 from uint256, reverting on
                       * overflow (when the input is greater than largest uint64).
                       *
                       * Counterpart to Solidity's `uint64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       */
                      function toUint64(uint256 value) internal pure returns (uint64) {
                        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                        return uint64(value);
                      }
                      /**
                       * @dev Returns the downcasted uint32 from uint256, reverting on
                       * overflow (when the input is greater than largest uint32).
                       *
                       * Counterpart to Solidity's `uint32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       */
                      function toUint32(uint256 value) internal pure returns (uint32) {
                        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                        return uint32(value);
                      }
                      /**
                       * @dev Returns the downcasted uint16 from uint256, reverting on
                       * overflow (when the input is greater than largest uint16).
                       *
                       * Counterpart to Solidity's `uint16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       */
                      function toUint16(uint256 value) internal pure returns (uint16) {
                        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                        return uint16(value);
                      }
                      /**
                       * @dev Returns the downcasted uint8 from uint256, reverting on
                       * overflow (when the input is greater than largest uint8).
                       *
                       * Counterpart to Solidity's `uint8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       */
                      function toUint8(uint256 value) internal pure returns (uint8) {
                        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                        return uint8(value);
                      }
                      /**
                       * @dev Converts a signed int256 into an unsigned uint256.
                       *
                       * Requirements:
                       *
                       * - input must be greater than or equal to 0.
                       */
                      function toUint256(int256 value) internal pure returns (uint256) {
                        require(value >= 0, 'SafeCast: value must be positive');
                        return uint256(value);
                      }
                      /**
                       * @dev Returns the downcasted int128 from int256, reverting on
                       * overflow (when the input is less than smallest int128 or
                       * greater than largest int128).
                       *
                       * Counterpart to Solidity's `int128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt128(int256 value) internal pure returns (int128) {
                        require(
                          value >= type(int128).min && value <= type(int128).max,
                          "SafeCast: value doesn't fit in 128 bits"
                        );
                        return int128(value);
                      }
                      /**
                       * @dev Returns the downcasted int64 from int256, reverting on
                       * overflow (when the input is less than smallest int64 or
                       * greater than largest int64).
                       *
                       * Counterpart to Solidity's `int64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt64(int256 value) internal pure returns (int64) {
                        require(
                          value >= type(int64).min && value <= type(int64).max,
                          "SafeCast: value doesn't fit in 64 bits"
                        );
                        return int64(value);
                      }
                      /**
                       * @dev Returns the downcasted int32 from int256, reverting on
                       * overflow (when the input is less than smallest int32 or
                       * greater than largest int32).
                       *
                       * Counterpart to Solidity's `int32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt32(int256 value) internal pure returns (int32) {
                        require(
                          value >= type(int32).min && value <= type(int32).max,
                          "SafeCast: value doesn't fit in 32 bits"
                        );
                        return int32(value);
                      }
                      /**
                       * @dev Returns the downcasted int16 from int256, reverting on
                       * overflow (when the input is less than smallest int16 or
                       * greater than largest int16).
                       *
                       * Counterpart to Solidity's `int16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt16(int256 value) internal pure returns (int16) {
                        require(
                          value >= type(int16).min && value <= type(int16).max,
                          "SafeCast: value doesn't fit in 16 bits"
                        );
                        return int16(value);
                      }
                      /**
                       * @dev Returns the downcasted int8 from int256, reverting on
                       * overflow (when the input is less than smallest int8 or
                       * greater than largest int8).
                       *
                       * Counterpart to Solidity's `int8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       *
                       * _Available since v3.1._
                       */
                      function toInt8(int256 value) internal pure returns (int8) {
                        require(
                          value >= type(int8).min && value <= type(int8).max,
                          "SafeCast: value doesn't fit in 8 bits"
                        );
                        return int8(value);
                      }
                      /**
                       * @dev Converts an unsigned uint256 into a signed int256.
                       *
                       * Requirements:
                       *
                       * - input must be less than or equal to maxInt256.
                       */
                      function toInt256(uint256 value) internal pure returns (int256) {
                        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                        return int256(value);
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    /**
                     * @title IACLManager
                     * @author Aave
                     * @notice Defines the basic interface for the ACL Manager
                     */
                    interface IACLManager {
                      /**
                       * @notice Returns the contract address of the PoolAddressesProvider
                       * @return The address of the PoolAddressesProvider
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Returns the identifier of the PoolAdmin role
                       * @return The id of the PoolAdmin role
                       */
                      function POOL_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the EmergencyAdmin role
                       * @return The id of the EmergencyAdmin role
                       */
                      function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the RiskAdmin role
                       * @return The id of the RiskAdmin role
                       */
                      function RISK_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the FlashBorrower role
                       * @return The id of the FlashBorrower role
                       */
                      function FLASH_BORROWER_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the Bridge role
                       * @return The id of the Bridge role
                       */
                      function BRIDGE_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the AssetListingAdmin role
                       * @return The id of the AssetListingAdmin role
                       */
                      function ASSET_LISTING_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Set the role as admin of a specific role.
                       * @dev By default the admin role for all roles is `DEFAULT_ADMIN_ROLE`.
                       * @param role The role to be managed by the admin role
                       * @param adminRole The admin role
                       */
                      function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
                      /**
                       * @notice Adds a new admin as PoolAdmin
                       * @param admin The address of the new admin
                       */
                      function addPoolAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as PoolAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removePoolAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is PoolAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is PoolAdmin, false otherwise
                       */
                      function isPoolAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new admin as EmergencyAdmin
                       * @param admin The address of the new admin
                       */
                      function addEmergencyAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as EmergencyAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeEmergencyAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is EmergencyAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is EmergencyAdmin, false otherwise
                       */
                      function isEmergencyAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new admin as RiskAdmin
                       * @param admin The address of the new admin
                       */
                      function addRiskAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as RiskAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeRiskAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is RiskAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is RiskAdmin, false otherwise
                       */
                      function isRiskAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new address as FlashBorrower
                       * @param borrower The address of the new FlashBorrower
                       */
                      function addFlashBorrower(address borrower) external;
                      /**
                       * @notice Removes an address as FlashBorrower
                       * @param borrower The address of the FlashBorrower to remove
                       */
                      function removeFlashBorrower(address borrower) external;
                      /**
                       * @notice Returns true if the address is FlashBorrower, false otherwise
                       * @param borrower The address to check
                       * @return True if the given address is FlashBorrower, false otherwise
                       */
                      function isFlashBorrower(address borrower) external view returns (bool);
                      /**
                       * @notice Adds a new address as Bridge
                       * @param bridge The address of the new Bridge
                       */
                      function addBridge(address bridge) external;
                      /**
                       * @notice Removes an address as Bridge
                       * @param bridge The address of the bridge to remove
                       */
                      function removeBridge(address bridge) external;
                      /**
                       * @notice Returns true if the address is Bridge, false otherwise
                       * @param bridge The address to check
                       * @return True if the given address is Bridge, false otherwise
                       */
                      function isBridge(address bridge) external view returns (bool);
                      /**
                       * @notice Adds a new admin as AssetListingAdmin
                       * @param admin The address of the new admin
                       */
                      function addAssetListingAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as AssetListingAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeAssetListingAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is AssetListingAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is AssetListingAdmin, false otherwise
                       */
                      function isAssetListingAdmin(address admin) external view returns (bool);
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    /**
                     * @title IAaveIncentivesController
                     * @author Aave
                     * @notice Defines the basic interface for an Aave Incentives Controller.
                     * @dev It only contains one single function, needed as a hook on aToken and debtToken transfers.
                     */
                    interface IAaveIncentivesController {
                      /**
                       * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
                       * @dev The units of `totalSupply` and `userBalance` should be the same.
                       * @param user The address of the user whose asset balance has changed
                       * @param totalSupply The total supply of the asset prior to user balance change
                       * @param userBalance The previous user balance prior to balance change
                       */
                      function handleAction(
                        address user,
                        uint256 totalSupply,
                        uint256 userBalance
                      ) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    /**
                     * @title ICreditDelegationToken
                     * @author Aave
                     * @notice Defines the basic interface for a token supporting credit delegation.
                     */
                    interface ICreditDelegationToken {
                      /**
                       * @dev Emitted on `approveDelegation` and `borrowAllowance
                       * @param fromUser The address of the delegator
                       * @param toUser The address of the delegatee
                       * @param asset The address of the delegated asset
                       * @param amount The amount being delegated
                       */
                      event BorrowAllowanceDelegated(
                        address indexed fromUser,
                        address indexed toUser,
                        address indexed asset,
                        uint256 amount
                      );
                      /**
                       * @notice Delegates borrowing power to a user on the specific debt token.
                       * Delegation will still respect the liquidation constraints (even if delegated, a
                       * delegatee cannot force a delegator HF to go below 1)
                       * @param delegatee The address receiving the delegated borrowing power
                       * @param amount The maximum amount being delegated.
                       */
                      function approveDelegation(address delegatee, uint256 amount) external;
                      /**
                       * @notice 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);
                      /**
                       * @notice Delegates borrowing power to a user on the specific debt token via ERC712 signature
                       * @param delegator The delegator of the credit
                       * @param delegatee The delegatee that can use the credit
                       * @param value The amount to be delegated
                       * @param deadline The deadline timestamp, type(uint256).max for max deadline
                       * @param v The V signature param
                       * @param s The S signature param
                       * @param r The R signature param
                       */
                      function delegationWithSig(
                        address delegator,
                        address delegatee,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                      ) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                    import {IPool} from './IPool.sol';
                    /**
                     * @title IInitializableDebtToken
                     * @author Aave
                     * @notice Interface for the initialize function common between debt tokens
                     */
                    interface IInitializableDebtToken {
                      /**
                       * @dev Emitted when a debt token is initialized
                       * @param underlyingAsset The address of the underlying asset
                       * @param pool The address of the associated 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
                      );
                      /**
                       * @notice Initializes the debt token.
                       * @param pool The pool contract that is initializing this contract
                       * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @param incentivesController The smart contract managing potential incentives distribution
                       * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's
                       * @param debtTokenName The name of the token
                       * @param debtTokenSymbol The symbol of the token
                       * @param params A set of encoded parameters for additional initialization
                       */
                      function initialize(
                        IPool pool,
                        address underlyingAsset,
                        IAaveIncentivesController incentivesController,
                        uint8 debtTokenDecimals,
                        string memory debtTokenName,
                        string memory debtTokenSymbol,
                        bytes calldata params
                      ) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                    /**
                     * @title IPool
                     * @author Aave
                     * @notice Defines the basic interface for an Aave Pool.
                     */
                    interface IPool {
                      /**
                       * @dev Emitted on mintUnbacked()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param user The address initiating the supply
                       * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens
                       * @param amount The amount of supplied assets
                       * @param referralCode The referral code used
                       */
                      event MintUnbacked(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted on backUnbacked()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param backer The address paying for the backing
                       * @param amount The amount added as backing
                       * @param fee The amount paid in fees
                       */
                      event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                      /**
                       * @dev Emitted on supply()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param user The address initiating the supply
                       * @param onBehalfOf The beneficiary of the supply, receiving the aTokens
                       * @param amount The amount supplied
                       * @param referralCode The referral code used
                       */
                      event Supply(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted on withdraw()
                       * @param reserve The address of the underlying asset being withdrawn
                       * @param user The address initiating the withdrawal, owner of aTokens
                       * @param to The 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 interestRateMode The rate mode: 1 for Stable, 2 for Variable
                       * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray
                       * @param referralCode The referral code used
                       */
                      event Borrow(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 borrowRate,
                        uint16 indexed referralCode
                      );
                      /**
                       * @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
                       * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
                       */
                      event Repay(
                        address indexed reserve,
                        address indexed user,
                        address indexed repayer,
                        uint256 amount,
                        bool useATokens
                      );
                      /**
                       * @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 interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable
                       */
                      event SwapBorrowRateMode(
                        address indexed reserve,
                        address indexed user,
                        DataTypes.InterestRateMode interestRateMode
                      );
                      /**
                       * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets
                       * @param asset The address of the underlying asset of the reserve
                       * @param totalDebt The total isolation mode debt for the reserve
                       */
                      event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                      /**
                       * @dev Emitted when the user selects a certain asset category for eMode
                       * @param user The address of the user
                       * @param categoryId The category id
                       */
                      event UserEModeSet(address indexed user, uint8 categoryId);
                      /**
                       * @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 interestRateMode The flashloan mode: 0 for regular flashloan, 1 for Stable debt, 2 for Variable debt
                       * @param premium The fee flash borrowed
                       * @param referralCode The referral code used
                       */
                      event FlashLoan(
                        address indexed target,
                        address initiator,
                        address indexed asset,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 premium,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted when a borrower is liquidated.
                       * @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 liquidator
                       * @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.
                       * @param reserve The address of the underlying asset of the reserve
                       * @param liquidityRate The next liquidity rate
                       * @param stableBorrowRate The next stable borrow rate
                       * @param variableBorrowRate The next variable borrow rate
                       * @param liquidityIndex The next liquidity index
                       * @param variableBorrowIndex The next variable borrow index
                       */
                      event ReserveDataUpdated(
                        address indexed reserve,
                        uint256 liquidityRate,
                        uint256 stableBorrowRate,
                        uint256 variableBorrowRate,
                        uint256 liquidityIndex,
                        uint256 variableBorrowIndex
                      );
                      /**
                       * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.
                       * @param reserve The address of the reserve
                       * @param amountMinted The amount minted to the treasury
                       */
                      event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                      /**
                       * @notice Mints an `amount` of aTokens to the `onBehalfOf`
                       * @param asset The address of the underlying asset to mint
                       * @param amount The amount to mint
                       * @param onBehalfOf The address that will receive the aTokens
                       * @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 mintUnbacked(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Back the current unbacked underlying with `amount` and pay `fee`.
                       * @param asset The address of the underlying asset to back
                       * @param amount The amount to back
                       * @param fee The amount paid in fees
                       * @return The backed amount
                       */
                      function backUnbacked(
                        address asset,
                        uint256 amount,
                        uint256 fee
                      ) external returns (uint256);
                      /**
                       * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                       * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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 supply(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Supply with transfer approval of asset to be supplied done via permit function
                       * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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 deadline The deadline timestamp that the permit is valid
                       * @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 permitV The V parameter of ERC712 permit sig
                       * @param permitR The R parameter of ERC712 permit sig
                       * @param permitS The S parameter of ERC712 permit sig
                       */
                      function supplyWithPermit(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) external;
                      /**
                       * @notice 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 The 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);
                      /**
                       * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                       * already supplied 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 The 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 The 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 interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                       * @param onBehalfOf The 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 interestRateMode,
                        address onBehalfOf
                      ) external returns (uint256);
                      /**
                       * @notice Repay with transfer approval of asset to be repaid done via permit function
                       * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                       * @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 interestRateMode 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
                       * @param deadline The deadline timestamp that the permit is valid
                       * @param permitV The V parameter of ERC712 permit sig
                       * @param permitR The R parameter of ERC712 permit sig
                       * @param permitS The S parameter of ERC712 permit sig
                       * @return The final amount repaid
                       */
                      function repayWithPermit(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode,
                        address onBehalfOf,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) external returns (uint256);
                      /**
                       * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
                       * equivalent debt tokens
                       * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable/stable debt tokens
                       * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
                       * balance is not enough to cover the whole debt
                       * @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 interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                       * @return The final amount repaid
                       */
                      function repayWithATokens(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode
                      ) external returns (uint256);
                      /**
                       * @notice Allows a borrower to swap his debt between stable and variable mode, or vice versa
                       * @param asset The address of the underlying asset borrowed
                       * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable
                       */
                      function swapBorrowRateMode(address asset, uint256 interestRateMode) external;
                      /**
                       * @notice 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 supply APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too
                       *        much has been borrowed at a stable rate and suppliers 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;
                      /**
                       * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
                       * @param asset The address of the underlying asset supplied
                       * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
                       */
                      function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                      /**
                       * @notice 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;
                      /**
                       * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                       * as long as the amount taken plus a fee is returned.
                       * @dev 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 IFlashLoanReceiver interface
                       * @param assets The addresses of the assets being flash-borrowed
                       * @param amounts The amounts of the assets being flash-borrowed
                       * @param interestRateModes 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 The 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 interestRateModes,
                        address onBehalfOf,
                        bytes calldata params,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                       * as long as the amount taken plus a fee is returned.
                       * @dev 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 IFlashLoanSimpleReceiver interface
                       * @param asset The address of the asset being flash-borrowed
                       * @param amount The amount of the asset being flash-borrowed
                       * @param params Variadic packed params to pass to the receiver as extra information
                       * @param referralCode The 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 flashLoanSimple(
                        address receiverAddress,
                        address asset,
                        uint256 amount,
                        bytes calldata params,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Returns the user account data across all the reserves
                       * @param user The address of the user
                       * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                       * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                       * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                       * @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 totalCollateralBase,
                          uint256 totalDebtBase,
                          uint256 availableBorrowsBase,
                          uint256 currentLiquidationThreshold,
                          uint256 ltv,
                          uint256 healthFactor
                        );
                      /**
                       * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                       * interest rate strategy
                       * @dev Only callable by the PoolConfigurator 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 variableDebtAddress 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;
                      /**
                       * @notice Drop a reserve
                       * @dev Only callable by the PoolConfigurator contract
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function dropReserve(address asset) external;
                      /**
                       * @notice Updates the address of the interest rate strategy contract
                       * @dev Only callable by the PoolConfigurator 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;
                      /**
                       * @notice Sets the configuration bitmap of the reserve as a whole
                       * @dev Only callable by the PoolConfigurator contract
                       * @param asset The address of the underlying asset of the reserve
                       * @param configuration The new configuration bitmap
                       */
                      function setConfiguration(address asset, DataTypes.ReserveConfigurationMap calldata configuration)
                        external;
                      /**
                       * @notice 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);
                      /**
                       * @notice 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);
                      /**
                       * @notice Returns the 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);
                      /**
                       * @notice Returns the normalized variable debt per unit of asset
                       * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a
                       * "dynamic" variable index based on time, current stored index and virtual rate at the current
                       * moment (approx. a borrower would get if opening a position). This means that is always used in
                       * combination with variable debt supply/balances.
                       * If using this function externally, consider that is possible to have an increasing normalized
                       * variable debt that is not equivalent to how the variable debt index would be updated in storage
                       * (e.g. only updates with non-zero variable debt supply)
                       * @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);
                      /**
                       * @notice Returns the state and configuration of the reserve
                       * @param asset The address of the underlying asset of the reserve
                       * @return The state and configuration data of the reserve
                       */
                      function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);
                      /**
                       * @notice Validates and finalizes an aToken transfer
                       * @dev 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;
                      /**
                       * @notice Returns the list of the underlying assets of all the initialized reserves
                       * @dev It does not include dropped reserves
                       * @return The addresses of the underlying assets of the initialized reserves
                       */
                      function getReservesList() external view returns (address[] memory);
                      /**
                       * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct
                       * @param id The id of the reserve as stored in the DataTypes.ReserveData struct
                       * @return The address of the reserve associated with id
                       */
                      function getReserveAddressById(uint16 id) external view returns (address);
                      /**
                       * @notice Returns the PoolAddressesProvider connected to this contract
                       * @return The address of the PoolAddressesProvider
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Updates the protocol fee on the bridging
                       * @param bridgeProtocolFee The part of the premium sent to the protocol treasury
                       */
                      function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;
                      /**
                       * @notice Updates flash loan premiums. Flash loan premium consists of two parts:
                       * - A part is sent to aToken holders as extra, one time accumulated interest
                       * - A part is collected by the protocol treasury
                       * @dev The total premium is calculated on the total borrowed amount
                       * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`
                       * @dev Only callable by the PoolConfigurator contract
                       * @param flashLoanPremiumTotal The total premium, expressed in bps
                       * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps
                       */
                      function updateFlashloanPremiums(
                        uint128 flashLoanPremiumTotal,
                        uint128 flashLoanPremiumToProtocol
                      ) external;
                      /**
                       * @notice Configures a new category for the eMode.
                       * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.
                       * The category 0 is reserved as it's the default for volatile assets
                       * @param id The id of the category
                       * @param config The configuration of the category
                       */
                      function configureEModeCategory(uint8 id, DataTypes.EModeCategory memory config) external;
                      /**
                       * @notice Returns the data of an eMode category
                       * @param id The id of the category
                       * @return The configuration data of the category
                       */
                      function getEModeCategoryData(uint8 id) external view returns (DataTypes.EModeCategory memory);
                      /**
                       * @notice Allows a user to use the protocol in eMode
                       * @param categoryId The id of the category
                       */
                      function setUserEMode(uint8 categoryId) external;
                      /**
                       * @notice Returns the eMode the user is using
                       * @param user The address of the user
                       * @return The eMode id
                       */
                      function getUserEMode(address user) external view returns (uint256);
                      /**
                       * @notice Resets the isolation mode total debt of the given asset to zero
                       * @dev It requires the given asset has zero debt ceiling
                       * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                       */
                      function resetIsolationModeTotalDebt(address asset) external;
                      /**
                       * @notice Returns the percentage of available liquidity that can be borrowed at once at stable rate
                       * @return The percentage of available liquidity to borrow, expressed in bps
                       */
                      function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() external view returns (uint256);
                      /**
                       * @notice Returns the total fee on flash loans
                       * @return The total fee on flashloans
                       */
                      function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);
                      /**
                       * @notice Returns the part of the bridge fees sent to protocol
                       * @return The bridge fee sent to the protocol treasury
                       */
                      function BRIDGE_PROTOCOL_FEE() external view returns (uint256);
                      /**
                       * @notice Returns the part of the flashloan fees sent to protocol
                       * @return The flashloan fee sent to the protocol treasury
                       */
                      function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);
                      /**
                       * @notice Returns the maximum number of reserves supported to be listed in this Pool
                       * @return The maximum number of reserves supported
                       */
                      function MAX_NUMBER_RESERVES() external view returns (uint16);
                      /**
                       * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                       * @param assets The list of reserves for which the minting needs to be executed
                       */
                      function mintToTreasury(address[] calldata assets) external;
                      /**
                       * @notice Rescue and transfer tokens locked in this contract
                       * @param token The address of the token
                       * @param to The address of the recipient
                       * @param amount The amount of token to transfer
                       */
                      function rescueTokens(
                        address token,
                        address to,
                        uint256 amount
                      ) external;
                      /**
                       * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                       * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                       * @dev Deprecated: Use the `supply` function instead
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    /**
                     * @title IPoolAddressesProvider
                     * @author Aave
                     * @notice Defines the basic interface for a Pool Addresses Provider.
                     */
                    interface IPoolAddressesProvider {
                      /**
                       * @dev Emitted when the market identifier is updated.
                       * @param oldMarketId The old id of the market
                       * @param newMarketId The new id of the market
                       */
                      event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                      /**
                       * @dev Emitted when the pool is updated.
                       * @param oldAddress The old address of the Pool
                       * @param newAddress The new address of the Pool
                       */
                      event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool configurator is updated.
                       * @param oldAddress The old address of the PoolConfigurator
                       * @param newAddress The new address of the PoolConfigurator
                       */
                      event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle is updated.
                       * @param oldAddress The old address of the PriceOracle
                       * @param newAddress The new address of the PriceOracle
                       */
                      event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL manager is updated.
                       * @param oldAddress The old address of the ACLManager
                       * @param newAddress The new address of the ACLManager
                       */
                      event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL admin is updated.
                       * @param oldAddress The old address of the ACLAdmin
                       * @param newAddress The new address of the ACLAdmin
                       */
                      event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle sentinel is updated.
                       * @param oldAddress The old address of the PriceOracleSentinel
                       * @param newAddress The new address of the PriceOracleSentinel
                       */
                      event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool data provider is updated.
                       * @param oldAddress The old address of the PoolDataProvider
                       * @param newAddress The new address of the PoolDataProvider
                       */
                      event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when a new proxy is created.
                       * @param id The identifier of the proxy
                       * @param proxyAddress The address of the created proxy contract
                       * @param implementationAddress The address of the implementation contract
                       */
                      event ProxyCreated(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address indexed implementationAddress
                      );
                      /**
                       * @dev Emitted when a new non-proxied contract address is registered.
                       * @param id The identifier of the contract
                       * @param oldAddress The address of the old contract
                       * @param newAddress The address of the new contract
                       */
                      event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the implementation of the proxy registered with id is updated
                       * @param id The identifier of the contract
                       * @param proxyAddress The address of the proxy contract
                       * @param oldImplementationAddress The address of the old implementation contract
                       * @param newImplementationAddress The address of the new implementation contract
                       */
                      event AddressSetAsProxy(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address oldImplementationAddress,
                        address indexed newImplementationAddress
                      );
                      /**
                       * @notice Returns the id of the Aave market to which this contract points to.
                       * @return The market id
                       */
                      function getMarketId() external view returns (string memory);
                      /**
                       * @notice Associates an id with a specific PoolAddressesProvider.
                       * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                       * identify and validate multiple Aave markets.
                       * @param newMarketId The market id
                       */
                      function setMarketId(string calldata newMarketId) external;
                      /**
                       * @notice Returns an address by its identifier.
                       * @dev The returned address might be an EOA or a contract, potentially proxied
                       * @dev It returns ZERO if there is no registered address with the given id
                       * @param id The id
                       * @return The address of the registered for the specified id
                       */
                      function getAddress(bytes32 id) external view returns (address);
                      /**
                       * @notice 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 `newImplementationAddress`.
                       * @dev 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 newImplementationAddress The address of the new implementation
                       */
                      function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                      /**
                       * @notice Sets an address for an id replacing the address saved in the addresses map.
                       * @dev 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;
                      /**
                       * @notice Returns the address of the Pool proxy.
                       * @return The Pool proxy address
                       */
                      function getPool() external view returns (address);
                      /**
                       * @notice Updates the implementation of the Pool, or creates a proxy
                       * setting the new `pool` implementation when the function is called for the first time.
                       * @param newPoolImpl The new Pool implementation
                       */
                      function setPoolImpl(address newPoolImpl) external;
                      /**
                       * @notice Returns the address of the PoolConfigurator proxy.
                       * @return The PoolConfigurator proxy address
                       */
                      function getPoolConfigurator() external view returns (address);
                      /**
                       * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                       * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                       * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                       */
                      function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                      /**
                       * @notice Returns the address of the price oracle.
                       * @return The address of the PriceOracle
                       */
                      function getPriceOracle() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle.
                       * @param newPriceOracle The address of the new PriceOracle
                       */
                      function setPriceOracle(address newPriceOracle) external;
                      /**
                       * @notice Returns the address of the ACL manager.
                       * @return The address of the ACLManager
                       */
                      function getACLManager() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL manager.
                       * @param newAclManager The address of the new ACLManager
                       */
                      function setACLManager(address newAclManager) external;
                      /**
                       * @notice Returns the address of the ACL admin.
                       * @return The address of the ACL admin
                       */
                      function getACLAdmin() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL admin.
                       * @param newAclAdmin The address of the new ACL admin
                       */
                      function setACLAdmin(address newAclAdmin) external;
                      /**
                       * @notice Returns the address of the price oracle sentinel.
                       * @return The address of the PriceOracleSentinel
                       */
                      function getPriceOracleSentinel() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle sentinel.
                       * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                       */
                      function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                      /**
                       * @notice Returns the address of the data provider.
                       * @return The address of the DataProvider
                       */
                      function getPoolDataProvider() external view returns (address);
                      /**
                       * @notice Updates the address of the data provider.
                       * @param newDataProvider The address of the new DataProvider
                       */
                      function setPoolDataProvider(address newDataProvider) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    /**
                     * @title IScaledBalanceToken
                     * @author Aave
                     * @notice Defines the basic interface for a scaled-balance token.
                     */
                    interface IScaledBalanceToken {
                      /**
                       * @dev Emitted after the mint action
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the minted tokens
                       * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
                       * @param index The next liquidity index of the reserve
                       */
                      event Mint(
                        address indexed caller,
                        address indexed onBehalfOf,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @dev Emitted after the burn action
                       * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
                       * @param from The address from which the tokens will be burned
                       * @param target The address that will receive the underlying, if any
                       * @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
                       * @param index The next liquidity index of the reserve
                       */
                      event Burn(
                        address indexed from,
                        address indexed target,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @notice Returns the scaled balance of the user.
                       * @dev 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);
                      /**
                       * @notice 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 total supply
                       */
                      function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                      /**
                       * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
                       * @return The scaled total supply
                       */
                      function scaledTotalSupply() external view returns (uint256);
                      /**
                       * @notice Returns last index interest was accrued to the user's balance
                       * @param user The address of the user
                       * @return The last index interest was accrued to the user's balance, expressed in ray
                       */
                      function getPreviousIndex(address user) external view returns (uint256);
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                    import {IInitializableDebtToken} from './IInitializableDebtToken.sol';
                    /**
                     * @title IVariableDebtToken
                     * @author Aave
                     * @notice Defines the basic interface for a variable debt token.
                     */
                    interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken {
                      /**
                       * @notice 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 previous balance of the user is 0, false otherwise
                       * @return The scaled total debt of the reserve
                       */
                      function mint(
                        address user,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) external returns (bool, uint256);
                      /**
                       * @notice Burns user variable debt
                       * @dev In some instances, a burn transaction will emit a mint event
                       * if the amount to burn is less than the interest that the user accrued
                       * @param from The address from which the debt will be burned
                       * @param amount The amount getting burned
                       * @param index The variable debt index of the reserve
                       * @return The scaled total debt of the reserve
                       */
                      function burn(
                        address from,
                        uint256 amount,
                        uint256 index
                      ) external returns (uint256);
                      /**
                       * @notice Returns the address of the underlying asset of this debtToken (E.g. WETH for variableDebtWETH)
                       * @return The address of the underlying asset
                       */
                      function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    /**
                     * @title VersionedInitializable
                     * @author Aave, inspired by the OpenZeppelin Initializable contract
                     * @notice Helper contract to implement initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * @dev 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.
                     */
                    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;
                        }
                      }
                      /**
                       * @notice Returns the revision number of the contract
                       * @dev Needs to be defined in the inherited class as a constant.
                       * @return The revision number
                       */
                      function getRevision() internal pure virtual returns (uint256);
                      /**
                       * @notice Returns true if and only if the function is running in the constructor
                       * @return True 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: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @title Errors library
                     * @author Aave
                     * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                     */
                    library Errors {
                      string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                      string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                      string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                      string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                      string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                      string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                      string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                      string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                      string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                      string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                      string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                      string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                      string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                      string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                      string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                      string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                      string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                      string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                      string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                      string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                      string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                      string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                      string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                      string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                      string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                      string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                      string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                      string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                      string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                      string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                      string public constant STABLE_BORROWING_NOT_ENABLED = '31'; // 'Stable borrowing is not enabled'
                      string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                      string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                      string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                      string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                      string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                      string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                      string public constant AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '38'; // 'The requested amount is greater than the max loan size in stable rate mode'
                      string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                      string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                      string public constant NO_OUTSTANDING_STABLE_DEBT = '41'; // 'User does not have outstanding stable rate debt on this reserve'
                      string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                      string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                      string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                      string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                      string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                      string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                      string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                      string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                      string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                      string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                      string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                      string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                      string public constant STABLE_DEBT_NOT_ZERO = '55'; // 'Stable debt supply is not zero'
                      string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                      string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                      string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                      string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                      string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                      string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                      string public constant USER_IN_ISOLATION_MODE = '62'; // 'User is in isolation mode'
                      string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                      string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                      string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                      string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                      string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                      string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                      string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                      string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                      string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                      string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                      string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                      string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                      string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                      string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                      string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                      string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                      string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                      string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                      string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                      string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                      string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                      string public constant INVALID_OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO = '84'; // 'Invalid optimal stable to total debt ratio'
                      string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                      string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                      string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                      string public constant STABLE_BORROWING_ENABLED = '88'; // 'Stable borrowing is enabled'
                      string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                      string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                      string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @title WadRayMath library
                     * @author Aave
                     * @notice Provides functions to perform calculations with Wad and Ray units
                     * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                     * with 27 digits of precision)
                     * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                     */
                    library WadRayMath {
                      // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                      uint256 internal constant WAD = 1e18;
                      uint256 internal constant HALF_WAD = 0.5e18;
                      uint256 internal constant RAY = 1e27;
                      uint256 internal constant HALF_RAY = 0.5e27;
                      uint256 internal constant WAD_RAY_RATIO = 1e9;
                      /**
                       * @dev Multiplies two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a*b, in wad
                       */
                      function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_WAD), WAD)
                        }
                      }
                      /**
                       * @dev Divides two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a/b, in wad
                       */
                      function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, WAD), div(b, 2)), b)
                        }
                      }
                      /**
                       * @notice Multiplies two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raymul b
                       */
                      function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_RAY), RAY)
                        }
                      }
                      /**
                       * @notice Divides two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raydiv b
                       */
                      function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, RAY), div(b, 2)), b)
                        }
                      }
                      /**
                       * @dev Casts ray down to wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @return b = a converted to wad, rounded half up to the nearest wad
                       */
                      function rayToWad(uint256 a) internal pure returns (uint256 b) {
                        assembly {
                          b := div(a, WAD_RAY_RATIO)
                          let remainder := mod(a, WAD_RAY_RATIO)
                          if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                            b := add(b, 1)
                          }
                        }
                      }
                      /**
                       * @dev Converts wad up to ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @return b = a converted in ray
                       */
                      function wadToRay(uint256 a) internal pure returns (uint256 b) {
                        // to avoid overflow, b/WAD_RAY_RATIO == a
                        assembly {
                          b := mul(a, WAD_RAY_RATIO)
                          if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                            revert(0, 0)
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    library DataTypes {
                      struct ReserveData {
                        //stores the reserve configuration
                        ReserveConfigurationMap configuration;
                        //the liquidity index. Expressed in ray
                        uint128 liquidityIndex;
                        //the current supply rate. Expressed in ray
                        uint128 currentLiquidityRate;
                        //variable borrow index. Expressed in ray
                        uint128 variableBorrowIndex;
                        //the current variable borrow rate. Expressed in ray
                        uint128 currentVariableBorrowRate;
                        //the current stable borrow rate. Expressed in ray
                        uint128 currentStableBorrowRate;
                        //timestamp of last update
                        uint40 lastUpdateTimestamp;
                        //the id of the reserve. Represents the position in the list of the active reserves
                        uint16 id;
                        //aToken address
                        address aTokenAddress;
                        //stableDebtToken address
                        address stableDebtTokenAddress;
                        //variableDebtToken address
                        address variableDebtTokenAddress;
                        //address of the interest rate strategy
                        address interestRateStrategyAddress;
                        //the current treasury balance, scaled
                        uint128 accruedToTreasury;
                        //the outstanding unbacked aTokens minted through the bridging feature
                        uint128 unbacked;
                        //the outstanding debt borrowed against this asset in isolation mode
                        uint128 isolationModeTotalDebt;
                      }
                      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: asset is paused
                        //bit 61: borrowing in isolation mode is enabled
                        //bit 62-63: reserved
                        //bit 64-79: reserve factor
                        //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap
                        //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap
                        //bit 152-167 liquidation protocol fee
                        //bit 168-175 eMode category
                        //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                        //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                        //bit 252-255 unused
                        uint256 data;
                      }
                      struct UserConfigurationMap {
                        /**
                         * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                         * The first bit indicates if an asset is used as collateral by the user, the second whether an
                         * asset is borrowed by the user.
                         */
                        uint256 data;
                      }
                      struct EModeCategory {
                        // each eMode category has a custom ltv and liquidation threshold
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        // each eMode category may or may not have a custom oracle to override the individual assets price oracles
                        address priceSource;
                        string label;
                      }
                      enum InterestRateMode {
                        NONE,
                        STABLE,
                        VARIABLE
                      }
                      struct ReserveCache {
                        uint256 currScaledVariableDebt;
                        uint256 nextScaledVariableDebt;
                        uint256 currPrincipalStableDebt;
                        uint256 currAvgStableBorrowRate;
                        uint256 currTotalStableDebt;
                        uint256 nextAvgStableBorrowRate;
                        uint256 nextTotalStableDebt;
                        uint256 currLiquidityIndex;
                        uint256 nextLiquidityIndex;
                        uint256 currVariableBorrowIndex;
                        uint256 nextVariableBorrowIndex;
                        uint256 currLiquidityRate;
                        uint256 currVariableBorrowRate;
                        uint256 reserveFactor;
                        ReserveConfigurationMap reserveConfiguration;
                        address aTokenAddress;
                        address stableDebtTokenAddress;
                        address variableDebtTokenAddress;
                        uint40 reserveLastUpdateTimestamp;
                        uint40 stableDebtLastUpdateTimestamp;
                      }
                      struct ExecuteLiquidationCallParams {
                        uint256 reservesCount;
                        uint256 debtToCover;
                        address collateralAsset;
                        address debtAsset;
                        address user;
                        bool receiveAToken;
                        address priceOracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteSupplyParams {
                        address asset;
                        uint256 amount;
                        address onBehalfOf;
                        uint16 referralCode;
                      }
                      struct ExecuteBorrowParams {
                        address asset;
                        address user;
                        address onBehalfOf;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint16 referralCode;
                        bool releaseUnderlying;
                        uint256 maxStableRateBorrowSizePercent;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteRepayParams {
                        address asset;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        address onBehalfOf;
                        bool useATokens;
                      }
                      struct ExecuteWithdrawParams {
                        address asset;
                        uint256 amount;
                        address to;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ExecuteSetUserEModeParams {
                        uint256 reservesCount;
                        address oracle;
                        uint8 categoryId;
                      }
                      struct FinalizeTransferParams {
                        address asset;
                        address from;
                        address to;
                        uint256 amount;
                        uint256 balanceFromBefore;
                        uint256 balanceToBefore;
                        uint256 reservesCount;
                        address oracle;
                        uint8 fromEModeCategory;
                      }
                      struct FlashloanParams {
                        address receiverAddress;
                        address[] assets;
                        uint256[] amounts;
                        uint256[] interestRateModes;
                        address onBehalfOf;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                        uint256 maxStableRateBorrowSizePercent;
                        uint256 reservesCount;
                        address addressesProvider;
                        uint8 userEModeCategory;
                        bool isAuthorizedFlashBorrower;
                      }
                      struct FlashloanSimpleParams {
                        address receiverAddress;
                        address asset;
                        uint256 amount;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                      }
                      struct FlashLoanRepaymentParams {
                        uint256 amount;
                        uint256 totalPremium;
                        uint256 flashLoanPremiumToProtocol;
                        address asset;
                        address receiverAddress;
                        uint16 referralCode;
                      }
                      struct CalculateUserAccountDataParams {
                        UserConfigurationMap userConfig;
                        uint256 reservesCount;
                        address user;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ValidateBorrowParams {
                        ReserveCache reserveCache;
                        UserConfigurationMap userConfig;
                        address asset;
                        address userAddress;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint256 maxStableLoanPercent;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                        bool isolationModeActive;
                        address isolationModeCollateralAddress;
                        uint256 isolationModeDebtCeiling;
                      }
                      struct ValidateLiquidationCallParams {
                        ReserveCache debtReserveCache;
                        uint256 totalDebt;
                        uint256 healthFactor;
                        address priceOracleSentinel;
                      }
                      struct CalculateInterestRatesParams {
                        uint256 unbacked;
                        uint256 liquidityAdded;
                        uint256 liquidityTaken;
                        uint256 totalStableDebt;
                        uint256 totalVariableDebt;
                        uint256 averageStableBorrowRate;
                        uint256 reserveFactor;
                        address reserve;
                        address aToken;
                      }
                      struct InitReserveParams {
                        address asset;
                        address aTokenAddress;
                        address stableDebtAddress;
                        address variableDebtAddress;
                        address interestRateStrategyAddress;
                        uint16 reservesCount;
                        uint16 maxNumberReserves;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {SafeCast} from '../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
                    import {WadRayMath} from '../libraries/math/WadRayMath.sol';
                    import {Errors} from '../libraries/helpers/Errors.sol';
                    import {IPool} from '../../interfaces/IPool.sol';
                    import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
                    import {IInitializableDebtToken} from '../../interfaces/IInitializableDebtToken.sol';
                    import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol';
                    import {EIP712Base} from './base/EIP712Base.sol';
                    import {DebtTokenBase} from './base/DebtTokenBase.sol';
                    import {ScaledBalanceTokenBase} from './base/ScaledBalanceTokenBase.sol';
                    /**
                     * @title VariableDebtToken
                     * @author Aave
                     * @notice Implements a variable debt token to track the borrowing positions of users
                     * at variable rate mode
                     * @dev Transfer and approve functionalities are disabled since its a non-transferable token
                     */
                    contract VariableDebtToken is DebtTokenBase, ScaledBalanceTokenBase, IVariableDebtToken {
                      using WadRayMath for uint256;
                      using SafeCast for uint256;
                      uint256 public constant DEBT_TOKEN_REVISION = 0x1;
                      /**
                       * @dev Constructor.
                       * @param pool The address of the Pool contract
                       */
                      constructor(IPool pool)
                        DebtTokenBase()
                        ScaledBalanceTokenBase(pool, 'VARIABLE_DEBT_TOKEN_IMPL', 'VARIABLE_DEBT_TOKEN_IMPL', 0)
                      {
                        // Intentionally left blank
                      }
                      /// @inheritdoc IInitializableDebtToken
                      function initialize(
                        IPool initializingPool,
                        address underlyingAsset,
                        IAaveIncentivesController incentivesController,
                        uint8 debtTokenDecimals,
                        string memory debtTokenName,
                        string memory debtTokenSymbol,
                        bytes calldata params
                      ) external override initializer {
                        require(initializingPool == POOL, Errors.POOL_ADDRESSES_DO_NOT_MATCH);
                        _setName(debtTokenName);
                        _setSymbol(debtTokenSymbol);
                        _setDecimals(debtTokenDecimals);
                        _underlyingAsset = underlyingAsset;
                        _incentivesController = incentivesController;
                        _domainSeparator = _calculateDomainSeparator();
                        emit Initialized(
                          underlyingAsset,
                          address(POOL),
                          address(incentivesController),
                          debtTokenDecimals,
                          debtTokenName,
                          debtTokenSymbol,
                          params
                        );
                      }
                      /// @inheritdoc VersionedInitializable
                      function getRevision() internal pure virtual override returns (uint256) {
                        return DEBT_TOKEN_REVISION;
                      }
                      /// @inheritdoc IERC20
                      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(_underlyingAsset));
                      }
                      /// @inheritdoc IVariableDebtToken
                      function mint(
                        address user,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) external virtual override onlyPool returns (bool, uint256) {
                        if (user != onBehalfOf) {
                          _decreaseBorrowAllowance(onBehalfOf, user, amount);
                        }
                        return (_mintScaled(user, onBehalfOf, amount, index), scaledTotalSupply());
                      }
                      /// @inheritdoc IVariableDebtToken
                      function burn(
                        address from,
                        uint256 amount,
                        uint256 index
                      ) external virtual override onlyPool returns (uint256) {
                        _burnScaled(from, address(0), amount, index);
                        return scaledTotalSupply();
                      }
                      /// @inheritdoc IERC20
                      function totalSupply() public view virtual override returns (uint256) {
                        return super.totalSupply().rayMul(POOL.getReserveNormalizedVariableDebt(_underlyingAsset));
                      }
                      /// @inheritdoc EIP712Base
                      function _EIP712BaseId() internal view override returns (string memory) {
                        return name();
                      }
                      /**
                       * @dev Being non transferrable, the debt token does not implement any of the
                       * standard ERC20 functions for transfer and allowance.
                       */
                      function transfer(address, uint256) external virtual override returns (bool) {
                        revert(Errors.OPERATION_NOT_SUPPORTED);
                      }
                      function allowance(address, address) external view virtual override returns (uint256) {
                        revert(Errors.OPERATION_NOT_SUPPORTED);
                      }
                      function approve(address, uint256) external virtual override returns (bool) {
                        revert(Errors.OPERATION_NOT_SUPPORTED);
                      }
                      function transferFrom(
                        address,
                        address,
                        uint256
                      ) external virtual override returns (bool) {
                        revert(Errors.OPERATION_NOT_SUPPORTED);
                      }
                      function increaseAllowance(address, uint256) external virtual override returns (bool) {
                        revert(Errors.OPERATION_NOT_SUPPORTED);
                      }
                      function decreaseAllowance(address, uint256) external virtual override returns (bool) {
                        revert(Errors.OPERATION_NOT_SUPPORTED);
                      }
                      /// @inheritdoc IVariableDebtToken
                      function UNDERLYING_ASSET_ADDRESS() external view override returns (address) {
                        return _underlyingAsset;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                    import {Errors} from '../../libraries/helpers/Errors.sol';
                    import {VersionedInitializable} from '../../libraries/aave-upgradeability/VersionedInitializable.sol';
                    import {ICreditDelegationToken} from '../../../interfaces/ICreditDelegationToken.sol';
                    import {EIP712Base} from './EIP712Base.sol';
                    /**
                     * @title DebtTokenBase
                     * @author Aave
                     * @notice Base contract for different types of debt tokens, like StableDebtToken or VariableDebtToken
                     */
                    abstract contract DebtTokenBase is
                      VersionedInitializable,
                      EIP712Base,
                      Context,
                      ICreditDelegationToken
                    {
                      // Map of borrow allowances (delegator => delegatee => borrowAllowanceAmount)
                      mapping(address => mapping(address => uint256)) internal _borrowAllowances;
                      // Credit Delegation Typehash
                      bytes32 public constant DELEGATION_WITH_SIG_TYPEHASH =
                        keccak256('DelegationWithSig(address delegatee,uint256 value,uint256 nonce,uint256 deadline)');
                      address internal _underlyingAsset;
                      /**
                       * @dev Constructor.
                       */
                      constructor() EIP712Base() {
                        // Intentionally left blank
                      }
                      /// @inheritdoc ICreditDelegationToken
                      function approveDelegation(address delegatee, uint256 amount) external override {
                        _approveDelegation(_msgSender(), delegatee, amount);
                      }
                      /// @inheritdoc ICreditDelegationToken
                      function delegationWithSig(
                        address delegator,
                        address delegatee,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                      ) external {
                        require(delegator != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                        //solium-disable-next-line
                        require(block.timestamp <= deadline, Errors.INVALID_EXPIRATION);
                        uint256 currentValidNonce = _nonces[delegator];
                        bytes32 digest = keccak256(
                          abi.encodePacked(
                            '\\x19\\x01',
                            DOMAIN_SEPARATOR(),
                            keccak256(
                              abi.encode(DELEGATION_WITH_SIG_TYPEHASH, delegatee, value, currentValidNonce, deadline)
                            )
                          )
                        );
                        require(delegator == ecrecover(digest, v, r, s), Errors.INVALID_SIGNATURE);
                        _nonces[delegator] = currentValidNonce + 1;
                        _approveDelegation(delegator, delegatee, value);
                      }
                      /// @inheritdoc ICreditDelegationToken
                      function borrowAllowance(address fromUser, address toUser)
                        external
                        view
                        override
                        returns (uint256)
                      {
                        return _borrowAllowances[fromUser][toUser];
                      }
                      /**
                       * @notice Updates the borrow allowance of a user on the specific debt token.
                       * @param delegator The address delegating the borrowing power
                       * @param delegatee The address receiving the delegated borrowing power
                       * @param amount The allowance amount being delegated.
                       */
                      function _approveDelegation(
                        address delegator,
                        address delegatee,
                        uint256 amount
                      ) internal {
                        _borrowAllowances[delegator][delegatee] = amount;
                        emit BorrowAllowanceDelegated(delegator, delegatee, _underlyingAsset, amount);
                      }
                      /**
                       * @notice Decreases the borrow allowance of a user on the specific debt token.
                       * @param delegator The address delegating the borrowing power
                       * @param delegatee The address receiving the delegated borrowing power
                       * @param amount The amount to subtract from the current allowance
                       */
                      function _decreaseBorrowAllowance(
                        address delegator,
                        address delegatee,
                        uint256 amount
                      ) internal {
                        uint256 newAllowance = _borrowAllowances[delegator][delegatee] - amount;
                        _borrowAllowances[delegator][delegatee] = newAllowance;
                        emit BorrowAllowanceDelegated(delegator, delegatee, _underlyingAsset, newAllowance);
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    /**
                     * @title EIP712Base
                     * @author Aave
                     * @notice Base contract implementation of EIP712.
                     */
                    abstract contract EIP712Base {
                      bytes public constant EIP712_REVISION = bytes('1');
                      bytes32 internal constant EIP712_DOMAIN =
                        keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
                      // Map of address nonces (address => nonce)
                      mapping(address => uint256) internal _nonces;
                      bytes32 internal _domainSeparator;
                      uint256 internal immutable _chainId;
                      /**
                       * @dev Constructor.
                       */
                      constructor() {
                        _chainId = block.chainid;
                      }
                      /**
                       * @notice Get the domain separator for the token
                       * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                       * @return The domain separator of the token at current chain
                       */
                      function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                        if (block.chainid == _chainId) {
                          return _domainSeparator;
                        }
                        return _calculateDomainSeparator();
                      }
                      /**
                       * @notice Returns the nonce value for address specified as parameter
                       * @param owner The address for which the nonce is being returned
                       * @return The nonce value for the input address`
                       */
                      function nonces(address owner) public view virtual returns (uint256) {
                        return _nonces[owner];
                      }
                      /**
                       * @notice Compute the current domain separator
                       * @return The domain separator for the token
                       */
                      function _calculateDomainSeparator() internal view returns (bytes32) {
                        return
                          keccak256(
                            abi.encode(
                              EIP712_DOMAIN,
                              keccak256(bytes(_EIP712BaseId())),
                              keccak256(EIP712_REVISION),
                              block.chainid,
                              address(this)
                            )
                          );
                      }
                      /**
                       * @notice Returns the user readable name of signing domain (e.g. token name)
                       * @return The name of the signing domain
                       */
                      function _EIP712BaseId() internal view virtual returns (string memory);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    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 {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                    import {Errors} from '../../libraries/helpers/Errors.sol';
                    import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IACLManager} from '../../../interfaces/IACLManager.sol';
                    /**
                     * @title IncentivizedERC20
                     * @author Aave, inspired by the Openzeppelin ERC20 implementation
                     * @notice Basic ERC20 implementation
                     */
                    abstract contract IncentivizedERC20 is Context, IERC20Detailed {
                      using WadRayMath for uint256;
                      using SafeCast for uint256;
                      /**
                       * @dev Only pool admin can call functions marked by this modifier.
                       */
                      modifier onlyPoolAdmin() {
                        IACLManager aclManager = IACLManager(_addressesProvider.getACLManager());
                        require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);
                        _;
                      }
                      /**
                       * @dev Only pool can call functions marked by this modifier.
                       */
                      modifier onlyPool() {
                        require(_msgSender() == address(POOL), Errors.CALLER_MUST_BE_POOL);
                        _;
                      }
                      /**
                       * @dev UserState - additionalData is a flexible field.
                       * ATokens and VariableDebtTokens use this field store the index of the
                       * user's last supply/withdrawal/borrow/repayment. StableDebtTokens use
                       * this field to store the user's stable rate.
                       */
                      struct UserState {
                        uint128 balance;
                        uint128 additionalData;
                      }
                      // Map of users address and their state data (userAddress => userStateData)
                      mapping(address => UserState) internal _userState;
                      // Map of allowances (delegator => delegatee => allowanceAmount)
                      mapping(address => mapping(address => uint256)) private _allowances;
                      uint256 internal _totalSupply;
                      string private _name;
                      string private _symbol;
                      uint8 private _decimals;
                      IAaveIncentivesController internal _incentivesController;
                      IPoolAddressesProvider internal immutable _addressesProvider;
                      IPool public immutable POOL;
                      /**
                       * @dev Constructor.
                       * @param pool The reference to the main Pool contract
                       * @param name The name of the token
                       * @param symbol The symbol of the token
                       * @param decimals The number of decimals of the token
                       */
                      constructor(
                        IPool pool,
                        string memory name,
                        string memory symbol,
                        uint8 decimals
                      ) {
                        _addressesProvider = pool.ADDRESSES_PROVIDER();
                        _name = name;
                        _symbol = symbol;
                        _decimals = decimals;
                        POOL = pool;
                      }
                      /// @inheritdoc IERC20Detailed
                      function name() public view override returns (string memory) {
                        return _name;
                      }
                      /// @inheritdoc IERC20Detailed
                      function symbol() external view override returns (string memory) {
                        return _symbol;
                      }
                      /// @inheritdoc IERC20Detailed
                      function decimals() external view override returns (uint8) {
                        return _decimals;
                      }
                      /// @inheritdoc IERC20
                      function totalSupply() public view virtual override returns (uint256) {
                        return _totalSupply;
                      }
                      /// @inheritdoc IERC20
                      function balanceOf(address account) public view virtual override returns (uint256) {
                        return _userState[account].balance;
                      }
                      /**
                       * @notice Returns the address of the Incentives Controller contract
                       * @return The address of the Incentives Controller
                       */
                      function getIncentivesController() external view virtual returns (IAaveIncentivesController) {
                        return _incentivesController;
                      }
                      /**
                       * @notice Sets a new Incentives Controller
                       * @param controller the new Incentives controller
                       */
                      function setIncentivesController(IAaveIncentivesController controller) external onlyPoolAdmin {
                        _incentivesController = controller;
                      }
                      /// @inheritdoc IERC20
                      function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
                        uint128 castAmount = amount.toUint128();
                        _transfer(_msgSender(), recipient, castAmount);
                        return true;
                      }
                      /// @inheritdoc IERC20
                      function allowance(address owner, address spender)
                        external
                        view
                        virtual
                        override
                        returns (uint256)
                      {
                        return _allowances[owner][spender];
                      }
                      /// @inheritdoc IERC20
                      function approve(address spender, uint256 amount) external virtual override returns (bool) {
                        _approve(_msgSender(), spender, amount);
                        return true;
                      }
                      /// @inheritdoc IERC20
                      function transferFrom(
                        address sender,
                        address recipient,
                        uint256 amount
                      ) external virtual override returns (bool) {
                        uint128 castAmount = amount.toUint128();
                        _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - castAmount);
                        _transfer(sender, recipient, castAmount);
                        return true;
                      }
                      /**
                       * @notice 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) external virtual returns (bool) {
                        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                        return true;
                      }
                      /**
                       * @notice 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)
                        external
                        virtual
                        returns (bool)
                      {
                        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
                        return true;
                      }
                      /**
                       * @notice Transfers tokens between two users and apply incentives if defined.
                       * @param sender The source address
                       * @param recipient The destination address
                       * @param amount The amount getting transferred
                       */
                      function _transfer(
                        address sender,
                        address recipient,
                        uint128 amount
                      ) internal virtual {
                        uint128 oldSenderBalance = _userState[sender].balance;
                        _userState[sender].balance = oldSenderBalance - amount;
                        uint128 oldRecipientBalance = _userState[recipient].balance;
                        _userState[recipient].balance = oldRecipientBalance + amount;
                        IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                        if (address(incentivesControllerLocal) != address(0)) {
                          uint256 currentTotalSupply = _totalSupply;
                          incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);
                          if (sender != recipient) {
                            incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                          }
                        }
                      }
                      /**
                       * @notice Approve `spender` to use `amount` of `owner`s balance
                       * @param owner The address owning the tokens
                       * @param spender The address approved for spending
                       * @param amount The amount of tokens to approve spending of
                       */
                      function _approve(
                        address owner,
                        address spender,
                        uint256 amount
                      ) internal virtual {
                        _allowances[owner][spender] = amount;
                        emit Approval(owner, spender, amount);
                      }
                      /**
                       * @notice Update the name of the token
                       * @param newName The new name for the token
                       */
                      function _setName(string memory newName) internal {
                        _name = newName;
                      }
                      /**
                       * @notice Update the symbol for the token
                       * @param newSymbol The new symbol for the token
                       */
                      function _setSymbol(string memory newSymbol) internal {
                        _symbol = newSymbol;
                      }
                      /**
                       * @notice Update the number of decimals for the token
                       * @param newDecimals The new number of decimals for the token
                       */
                      function _setDecimals(uint8 newDecimals) internal {
                        _decimals = newDecimals;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IncentivizedERC20} from './IncentivizedERC20.sol';
                    /**
                     * @title MintableIncentivizedERC20
                     * @author Aave
                     * @notice Implements mint and burn functions for IncentivizedERC20
                     */
                    abstract contract MintableIncentivizedERC20 is IncentivizedERC20 {
                      /**
                       * @dev Constructor.
                       * @param pool The reference to the main Pool contract
                       * @param name The name of the token
                       * @param symbol The symbol of the token
                       * @param decimals The number of decimals of the token
                       */
                      constructor(
                        IPool pool,
                        string memory name,
                        string memory symbol,
                        uint8 decimals
                      ) IncentivizedERC20(pool, name, symbol, decimals) {
                        // Intentionally left blank
                      }
                      /**
                       * @notice Mints tokens to an account and apply incentives if defined
                       * @param account The address receiving tokens
                       * @param amount The amount of tokens to mint
                       */
                      function _mint(address account, uint128 amount) internal virtual {
                        uint256 oldTotalSupply = _totalSupply;
                        _totalSupply = oldTotalSupply + amount;
                        uint128 oldAccountBalance = _userState[account].balance;
                        _userState[account].balance = oldAccountBalance + amount;
                        IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                        if (address(incentivesControllerLocal) != address(0)) {
                          incentivesControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);
                        }
                      }
                      /**
                       * @notice Burns tokens from an account and apply incentives if defined
                       * @param account The account whose tokens are burnt
                       * @param amount The amount of tokens to burn
                       */
                      function _burn(address account, uint128 amount) internal virtual {
                        uint256 oldTotalSupply = _totalSupply;
                        _totalSupply = oldTotalSupply - amount;
                        uint128 oldAccountBalance = _userState[account].balance;
                        _userState[account].balance = oldAccountBalance - amount;
                        IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                        if (address(incentivesControllerLocal) != address(0)) {
                          incentivesControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {Errors} from '../../libraries/helpers/Errors.sol';
                    import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                    import {MintableIncentivizedERC20} from './MintableIncentivizedERC20.sol';
                    /**
                     * @title ScaledBalanceTokenBase
                     * @author Aave
                     * @notice Basic ERC20 implementation of scaled balance token
                     */
                    abstract contract ScaledBalanceTokenBase is MintableIncentivizedERC20, IScaledBalanceToken {
                      using WadRayMath for uint256;
                      using SafeCast for uint256;
                      /**
                       * @dev Constructor.
                       * @param pool The reference to the main Pool contract
                       * @param name The name of the token
                       * @param symbol The symbol of the token
                       * @param decimals The number of decimals of the token
                       */
                      constructor(
                        IPool pool,
                        string memory name,
                        string memory symbol,
                        uint8 decimals
                      ) MintableIncentivizedERC20(pool, name, symbol, decimals) {
                        // Intentionally left blank
                      }
                      /// @inheritdoc IScaledBalanceToken
                      function scaledBalanceOf(address user) external view override returns (uint256) {
                        return super.balanceOf(user);
                      }
                      /// @inheritdoc IScaledBalanceToken
                      function getScaledUserBalanceAndSupply(address user)
                        external
                        view
                        override
                        returns (uint256, uint256)
                      {
                        return (super.balanceOf(user), super.totalSupply());
                      }
                      /// @inheritdoc IScaledBalanceToken
                      function scaledTotalSupply() public view virtual override returns (uint256) {
                        return super.totalSupply();
                      }
                      /// @inheritdoc IScaledBalanceToken
                      function getPreviousIndex(address user) external view virtual override returns (uint256) {
                        return _userState[user].additionalData;
                      }
                      /**
                       * @notice Implements the basic logic to mint a scaled balance token.
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the scaled tokens
                       * @param amount The amount of tokens getting minted
                       * @param index The next liquidity index of the reserve
                       * @return `true` if the the previous balance of the user was 0
                       */
                      function _mintScaled(
                        address caller,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) internal returns (bool) {
                        uint256 amountScaled = amount.rayDiv(index);
                        require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);
                        uint256 scaledBalance = super.balanceOf(onBehalfOf);
                        uint256 balanceIncrease = scaledBalance.rayMul(index) -
                          scaledBalance.rayMul(_userState[onBehalfOf].additionalData);
                        _userState[onBehalfOf].additionalData = index.toUint128();
                        _mint(onBehalfOf, amountScaled.toUint128());
                        uint256 amountToMint = amount + balanceIncrease;
                        emit Transfer(address(0), onBehalfOf, amountToMint);
                        emit Mint(caller, onBehalfOf, amountToMint, balanceIncrease, index);
                        return (scaledBalance == 0);
                      }
                      /**
                       * @notice Implements the basic logic to burn a scaled balance token.
                       * @dev In some instances, a burn transaction will emit a mint event
                       * if the amount to burn is less than the interest that the user accrued
                       * @param user The user which debt is burnt
                       * @param target The address that will receive the underlying, if any
                       * @param amount The amount getting burned
                       * @param index The variable debt index of the reserve
                       */
                      function _burnScaled(
                        address user,
                        address target,
                        uint256 amount,
                        uint256 index
                      ) internal {
                        uint256 amountScaled = amount.rayDiv(index);
                        require(amountScaled != 0, Errors.INVALID_BURN_AMOUNT);
                        uint256 scaledBalance = super.balanceOf(user);
                        uint256 balanceIncrease = scaledBalance.rayMul(index) -
                          scaledBalance.rayMul(_userState[user].additionalData);
                        _userState[user].additionalData = index.toUint128();
                        _burn(user, amountScaled.toUint128());
                        if (balanceIncrease > amount) {
                          uint256 amountToMint = balanceIncrease - amount;
                          emit Transfer(address(0), user, amountToMint);
                          emit Mint(user, user, amountToMint, balanceIncrease, index);
                        } else {
                          uint256 amountToBurn = amount - balanceIncrease;
                          emit Transfer(user, address(0), amountToBurn);
                          emit Burn(user, target, amountToBurn, balanceIncrease, index);
                        }
                      }
                      /**
                       * @notice Implements the basic logic to transfer scaled balance tokens between two users
                       * @dev It emits a mint event with the interest accrued per user
                       * @param sender The source address
                       * @param recipient The destination address
                       * @param amount The amount getting transferred
                       * @param index The next liquidity index of the reserve
                       */
                      function _transfer(
                        address sender,
                        address recipient,
                        uint256 amount,
                        uint256 index
                      ) internal {
                        uint256 senderScaledBalance = super.balanceOf(sender);
                        uint256 senderBalanceIncrease = senderScaledBalance.rayMul(index) -
                          senderScaledBalance.rayMul(_userState[sender].additionalData);
                        uint256 recipientScaledBalance = super.balanceOf(recipient);
                        uint256 recipientBalanceIncrease = recipientScaledBalance.rayMul(index) -
                          recipientScaledBalance.rayMul(_userState[recipient].additionalData);
                        _userState[sender].additionalData = index.toUint128();
                        _userState[recipient].additionalData = index.toUint128();
                        super._transfer(sender, recipient, amount.rayDiv(index).toUint128());
                        if (senderBalanceIncrease > 0) {
                          emit Transfer(address(0), sender, senderBalanceIncrease);
                          emit Mint(_msgSender(), sender, senderBalanceIncrease, senderBalanceIncrease, index);
                        }
                        if (sender != recipient && recipientBalanceIncrease > 0) {
                          emit Transfer(address(0), recipient, recipientBalanceIncrease);
                          emit Mint(_msgSender(), recipient, recipientBalanceIncrease, recipientBalanceIncrease, index);
                        }
                        emit Transfer(sender, recipient, amount);
                      }
                    }
                    

                    File 8 of 12: AToken
                    // SPDX-License-Identifier: LGPL-3.0-or-later
                    pragma solidity 0.8.10;
                    import {IERC20} from '../../openzeppelin/contracts/IERC20.sol';
                    /// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
                    /// @author Gnosis Developers
                    /// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract.
                    library GPv2SafeERC20 {
                      /// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
                      /// also when the token returns `false`.
                      function safeTransfer(
                        IERC20 token,
                        address to,
                        uint256 value
                      ) internal {
                        bytes4 selector_ = token.transfer.selector;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          let freeMemoryPointer := mload(0x40)
                          mstore(freeMemoryPointer, selector_)
                          mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 36), value)
                          if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
                            returndatacopy(0, 0, returndatasize())
                            revert(0, returndatasize())
                          }
                        }
                        require(getLastTransferResult(token), 'GPv2: failed transfer');
                      }
                      /// @dev Wrapper around a call to the ERC20 function `transferFrom` that
                      /// reverts also when the token returns `false`.
                      function safeTransferFrom(
                        IERC20 token,
                        address from,
                        address to,
                        uint256 value
                      ) internal {
                        bytes4 selector_ = token.transferFrom.selector;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          let freeMemoryPointer := mload(0x40)
                          mstore(freeMemoryPointer, selector_)
                          mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
                          mstore(add(freeMemoryPointer, 68), value)
                          if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
                            returndatacopy(0, 0, returndatasize())
                            revert(0, returndatasize())
                          }
                        }
                        require(getLastTransferResult(token), 'GPv2: failed transferFrom');
                      }
                      /// @dev Verifies that the last return was a successful `transfer*` call.
                      /// This is done by checking that the return data is either empty, or
                      /// is a valid ABI encoded boolean.
                      function getLastTransferResult(IERC20 token) private view returns (bool success) {
                        // NOTE: Inspecting previous return data requires assembly. Note that
                        // we write the return data to memory 0 in the case where the return
                        // data size is 32, this is OK since the first 64 bytes of memory are
                        // reserved by Solidy as a scratch space that can be used within
                        // assembly blocks.
                        // <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          /// @dev Revert with an ABI encoded Solidity error with a message
                          /// that fits into 32-bytes.
                          ///
                          /// An ABI encoded Solidity error has the following memory layout:
                          ///
                          /// ------------+----------------------------------
                          ///  byte range | value
                          /// ------------+----------------------------------
                          ///  0x00..0x04 |        selector("Error(string)")
                          ///  0x04..0x24 |      string offset (always 0x20)
                          ///  0x24..0x44 |                    string length
                          ///  0x44..0x64 | string value, padded to 32-bytes
                          function revertWithMessage(length, message) {
                            mstore(0x00, '\\x08\\xc3\\x79\\xa0')
                            mstore(0x04, 0x20)
                            mstore(0x24, length)
                            mstore(0x44, message)
                            revert(0x00, 0x64)
                          }
                          switch returndatasize()
                          // Non-standard ERC20 transfer without return.
                          case 0 {
                            // NOTE: When the return data size is 0, verify that there
                            // is code at the address. This is done in order to maintain
                            // compatibility with Solidity calling conventions.
                            // <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
                            if iszero(extcodesize(token)) {
                              revertWithMessage(20, 'GPv2: not a contract')
                            }
                            success := 1
                          }
                          // Standard ERC20 transfer returning boolean success value.
                          case 32 {
                            returndatacopy(0, 0, returndatasize())
                            // NOTE: For ABI encoding v1, any non-zero value is accepted
                            // as `true` for a boolean. In order to stay compatible with
                            // OpenZeppelin's `SafeERC20` library which is known to work
                            // with the existing ERC20 implementation we care about,
                            // make sure we return success for any non-zero return value
                            // from the `transfer*` call.
                            success := iszero(iszero(mload(0)))
                          }
                          default {
                            revertWithMessage(31, 'GPv2: malformed transfer result')
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity 0.8.10;
                    /*
                     * @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 payable(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.8.10;
                    /**
                     * @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.8.10;
                    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: MIT
                    // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                    pragma solidity 0.8.10;
                    /**
                     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                     * checks.
                     *
                     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                     * easily result in undesired exploitation or bugs, since developers usually
                     * assume that overflows raise errors. `SafeCast` restores this intuition by
                     * reverting the transaction when such 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.
                     *
                     * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                     * all math on `uint256` and `int256` and then downcasting.
                     */
                    library SafeCast {
                      /**
                       * @dev Returns the downcasted uint224 from uint256, reverting on
                       * overflow (when the input is greater than largest uint224).
                       *
                       * Counterpart to Solidity's `uint224` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 224 bits
                       */
                      function toUint224(uint256 value) internal pure returns (uint224) {
                        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                        return uint224(value);
                      }
                      /**
                       * @dev Returns the downcasted uint128 from uint256, reverting on
                       * overflow (when the input is greater than largest uint128).
                       *
                       * Counterpart to Solidity's `uint128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       */
                      function toUint128(uint256 value) internal pure returns (uint128) {
                        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                        return uint128(value);
                      }
                      /**
                       * @dev Returns the downcasted uint96 from uint256, reverting on
                       * overflow (when the input is greater than largest uint96).
                       *
                       * Counterpart to Solidity's `uint96` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 96 bits
                       */
                      function toUint96(uint256 value) internal pure returns (uint96) {
                        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                        return uint96(value);
                      }
                      /**
                       * @dev Returns the downcasted uint64 from uint256, reverting on
                       * overflow (when the input is greater than largest uint64).
                       *
                       * Counterpart to Solidity's `uint64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       */
                      function toUint64(uint256 value) internal pure returns (uint64) {
                        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                        return uint64(value);
                      }
                      /**
                       * @dev Returns the downcasted uint32 from uint256, reverting on
                       * overflow (when the input is greater than largest uint32).
                       *
                       * Counterpart to Solidity's `uint32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       */
                      function toUint32(uint256 value) internal pure returns (uint32) {
                        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                        return uint32(value);
                      }
                      /**
                       * @dev Returns the downcasted uint16 from uint256, reverting on
                       * overflow (when the input is greater than largest uint16).
                       *
                       * Counterpart to Solidity's `uint16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       */
                      function toUint16(uint256 value) internal pure returns (uint16) {
                        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                        return uint16(value);
                      }
                      /**
                       * @dev Returns the downcasted uint8 from uint256, reverting on
                       * overflow (when the input is greater than largest uint8).
                       *
                       * Counterpart to Solidity's `uint8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       */
                      function toUint8(uint256 value) internal pure returns (uint8) {
                        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                        return uint8(value);
                      }
                      /**
                       * @dev Converts a signed int256 into an unsigned uint256.
                       *
                       * Requirements:
                       *
                       * - input must be greater than or equal to 0.
                       */
                      function toUint256(int256 value) internal pure returns (uint256) {
                        require(value >= 0, 'SafeCast: value must be positive');
                        return uint256(value);
                      }
                      /**
                       * @dev Returns the downcasted int128 from int256, reverting on
                       * overflow (when the input is less than smallest int128 or
                       * greater than largest int128).
                       *
                       * Counterpart to Solidity's `int128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt128(int256 value) internal pure returns (int128) {
                        require(
                          value >= type(int128).min && value <= type(int128).max,
                          "SafeCast: value doesn't fit in 128 bits"
                        );
                        return int128(value);
                      }
                      /**
                       * @dev Returns the downcasted int64 from int256, reverting on
                       * overflow (when the input is less than smallest int64 or
                       * greater than largest int64).
                       *
                       * Counterpart to Solidity's `int64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt64(int256 value) internal pure returns (int64) {
                        require(
                          value >= type(int64).min && value <= type(int64).max,
                          "SafeCast: value doesn't fit in 64 bits"
                        );
                        return int64(value);
                      }
                      /**
                       * @dev Returns the downcasted int32 from int256, reverting on
                       * overflow (when the input is less than smallest int32 or
                       * greater than largest int32).
                       *
                       * Counterpart to Solidity's `int32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt32(int256 value) internal pure returns (int32) {
                        require(
                          value >= type(int32).min && value <= type(int32).max,
                          "SafeCast: value doesn't fit in 32 bits"
                        );
                        return int32(value);
                      }
                      /**
                       * @dev Returns the downcasted int16 from int256, reverting on
                       * overflow (when the input is less than smallest int16 or
                       * greater than largest int16).
                       *
                       * Counterpart to Solidity's `int16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt16(int256 value) internal pure returns (int16) {
                        require(
                          value >= type(int16).min && value <= type(int16).max,
                          "SafeCast: value doesn't fit in 16 bits"
                        );
                        return int16(value);
                      }
                      /**
                       * @dev Returns the downcasted int8 from int256, reverting on
                       * overflow (when the input is less than smallest int8 or
                       * greater than largest int8).
                       *
                       * Counterpart to Solidity's `int8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       *
                       * _Available since v3.1._
                       */
                      function toInt8(int256 value) internal pure returns (int8) {
                        require(
                          value >= type(int8).min && value <= type(int8).max,
                          "SafeCast: value doesn't fit in 8 bits"
                        );
                        return int8(value);
                      }
                      /**
                       * @dev Converts an unsigned uint256 into a signed int256.
                       *
                       * Requirements:
                       *
                       * - input must be less than or equal to maxInt256.
                       */
                      function toInt256(uint256 value) internal pure returns (int256) {
                        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                        return int256(value);
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    /**
                     * @title IACLManager
                     * @author Aave
                     * @notice Defines the basic interface for the ACL Manager
                     */
                    interface IACLManager {
                      /**
                       * @notice Returns the contract address of the PoolAddressesProvider
                       * @return The address of the PoolAddressesProvider
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Returns the identifier of the PoolAdmin role
                       * @return The id of the PoolAdmin role
                       */
                      function POOL_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the EmergencyAdmin role
                       * @return The id of the EmergencyAdmin role
                       */
                      function EMERGENCY_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the RiskAdmin role
                       * @return The id of the RiskAdmin role
                       */
                      function RISK_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the FlashBorrower role
                       * @return The id of the FlashBorrower role
                       */
                      function FLASH_BORROWER_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the Bridge role
                       * @return The id of the Bridge role
                       */
                      function BRIDGE_ROLE() external view returns (bytes32);
                      /**
                       * @notice Returns the identifier of the AssetListingAdmin role
                       * @return The id of the AssetListingAdmin role
                       */
                      function ASSET_LISTING_ADMIN_ROLE() external view returns (bytes32);
                      /**
                       * @notice Set the role as admin of a specific role.
                       * @dev By default the admin role for all roles is `DEFAULT_ADMIN_ROLE`.
                       * @param role The role to be managed by the admin role
                       * @param adminRole The admin role
                       */
                      function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
                      /**
                       * @notice Adds a new admin as PoolAdmin
                       * @param admin The address of the new admin
                       */
                      function addPoolAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as PoolAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removePoolAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is PoolAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is PoolAdmin, false otherwise
                       */
                      function isPoolAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new admin as EmergencyAdmin
                       * @param admin The address of the new admin
                       */
                      function addEmergencyAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as EmergencyAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeEmergencyAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is EmergencyAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is EmergencyAdmin, false otherwise
                       */
                      function isEmergencyAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new admin as RiskAdmin
                       * @param admin The address of the new admin
                       */
                      function addRiskAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as RiskAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeRiskAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is RiskAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is RiskAdmin, false otherwise
                       */
                      function isRiskAdmin(address admin) external view returns (bool);
                      /**
                       * @notice Adds a new address as FlashBorrower
                       * @param borrower The address of the new FlashBorrower
                       */
                      function addFlashBorrower(address borrower) external;
                      /**
                       * @notice Removes an address as FlashBorrower
                       * @param borrower The address of the FlashBorrower to remove
                       */
                      function removeFlashBorrower(address borrower) external;
                      /**
                       * @notice Returns true if the address is FlashBorrower, false otherwise
                       * @param borrower The address to check
                       * @return True if the given address is FlashBorrower, false otherwise
                       */
                      function isFlashBorrower(address borrower) external view returns (bool);
                      /**
                       * @notice Adds a new address as Bridge
                       * @param bridge The address of the new Bridge
                       */
                      function addBridge(address bridge) external;
                      /**
                       * @notice Removes an address as Bridge
                       * @param bridge The address of the bridge to remove
                       */
                      function removeBridge(address bridge) external;
                      /**
                       * @notice Returns true if the address is Bridge, false otherwise
                       * @param bridge The address to check
                       * @return True if the given address is Bridge, false otherwise
                       */
                      function isBridge(address bridge) external view returns (bool);
                      /**
                       * @notice Adds a new admin as AssetListingAdmin
                       * @param admin The address of the new admin
                       */
                      function addAssetListingAdmin(address admin) external;
                      /**
                       * @notice Removes an admin as AssetListingAdmin
                       * @param admin The address of the admin to remove
                       */
                      function removeAssetListingAdmin(address admin) external;
                      /**
                       * @notice Returns true if the address is AssetListingAdmin, false otherwise
                       * @param admin The address to check
                       * @return True if the given address is AssetListingAdmin, false otherwise
                       */
                      function isAssetListingAdmin(address admin) external view returns (bool);
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                    import {IInitializableAToken} from './IInitializableAToken.sol';
                    /**
                     * @title IAToken
                     * @author Aave
                     * @notice Defines the basic interface for an AToken.
                     */
                    interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken {
                      /**
                       * @dev Emitted during the transfer action
                       * @param from The user whose tokens are being transferred
                       * @param to The recipient
                       * @param value The scaled amount being transferred
                       * @param index The next liquidity index of the reserve
                       */
                      event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index);
                      /**
                       * @notice Mints `amount` aTokens to `user`
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the minted aTokens
                       * @param amount The amount of tokens getting minted
                       * @param index The next liquidity index of the reserve
                       * @return `true` if the the previous balance of the user was 0
                       */
                      function mint(
                        address caller,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) external returns (bool);
                      /**
                       * @notice Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                       * @dev In some instances, the mint event could be emitted from a burn transaction
                       * if the amount to burn is less than the interest that the user accrued
                       * @param from The address from which the aTokens will be burned
                       * @param receiverOfUnderlying The address that will receive the underlying
                       * @param amount The amount being burned
                       * @param index The next liquidity index of the reserve
                       */
                      function burn(
                        address from,
                        address receiverOfUnderlying,
                        uint256 amount,
                        uint256 index
                      ) external;
                      /**
                       * @notice Mints aTokens to the reserve treasury
                       * @param amount The amount of tokens getting minted
                       * @param index The next liquidity index of the reserve
                       */
                      function mintToTreasury(uint256 amount, uint256 index) external;
                      /**
                       * @notice 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;
                      /**
                       * @notice Transfers the underlying asset to `target`.
                       * @dev Used by the Pool to transfer assets in borrow(), withdraw() and flashLoan()
                       * @param target The recipient of the underlying
                       * @param amount The amount getting transferred
                       */
                      function transferUnderlyingTo(address target, uint256 amount) external;
                      /**
                       * @notice Handles the underlying received by the aToken after the transfer has been completed.
                       * @dev The default implementation is empty as with standard ERC20 tokens, nothing needs to be done after the
                       * transfer is concluded. However in the future there may be aTokens that allow for example to stake the underlying
                       * to receive LM rewards. In that case, `handleRepayment()` would perform the staking of the underlying asset.
                       * @param user The user executing the repayment
                       * @param onBehalfOf The address of the user who will get his debt reduced/removed
                       * @param amount The amount getting repaid
                       */
                      function handleRepayment(
                        address user,
                        address onBehalfOf,
                        uint256 amount
                      ) external;
                      /**
                       * @notice Allow passing a signed message to approve spending
                       * @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;
                      /**
                       * @notice Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @return The address of the underlying asset
                       */
                      function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                      /**
                       * @notice Returns the address of the Aave treasury, receiving the fees on this aToken.
                       * @return Address of the Aave treasury
                       */
                      function RESERVE_TREASURY_ADDRESS() external view returns (address);
                      /**
                       * @notice Get the domain separator for the token
                       * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                       * @return The domain separator of the token at current chain
                       */
                      function DOMAIN_SEPARATOR() external view returns (bytes32);
                      /**
                       * @notice Returns the nonce for owner.
                       * @param owner The address of the owner
                       * @return The nonce of the owner
                       */
                      function nonces(address owner) external view returns (uint256);
                      /**
                       * @notice Rescue and transfer tokens locked in this contract
                       * @param token The address of the token
                       * @param to The address of the recipient
                       * @param amount The amount of token to transfer
                       */
                      function rescueTokens(
                        address token,
                        address to,
                        uint256 amount
                      ) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    /**
                     * @title IAaveIncentivesController
                     * @author Aave
                     * @notice Defines the basic interface for an Aave Incentives Controller.
                     * @dev It only contains one single function, needed as a hook on aToken and debtToken transfers.
                     */
                    interface IAaveIncentivesController {
                      /**
                       * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
                       * @dev The units of `totalSupply` and `userBalance` should be the same.
                       * @param user The address of the user whose asset balance has changed
                       * @param totalSupply The total supply of the asset prior to user balance change
                       * @param userBalance The previous user balance prior to balance change
                       */
                      function handleAction(
                        address user,
                        uint256 totalSupply,
                        uint256 userBalance
                      ) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    import {IAaveIncentivesController} from './IAaveIncentivesController.sol';
                    import {IPool} from './IPool.sol';
                    /**
                     * @title IInitializableAToken
                     * @author Aave
                     * @notice Interface for the initialize function on AToken
                     */
                    interface IInitializableAToken {
                      /**
                       * @dev Emitted when an aToken is initialized
                       * @param underlyingAsset The address of the underlying asset
                       * @param pool The address of the associated 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
                      );
                      /**
                       * @notice Initializes the aToken
                       * @param pool The pool contract that is initializing this contract
                       * @param treasury The address of the Aave treasury, receiving the fees on this aToken
                       * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH)
                       * @param incentivesController The smart contract managing potential incentives distribution
                       * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's
                       * @param aTokenName The name of the aToken
                       * @param aTokenSymbol The symbol of the aToken
                       * @param params A set of encoded parameters for additional initialization
                       */
                      function initialize(
                        IPool pool,
                        address treasury,
                        address underlyingAsset,
                        IAaveIncentivesController incentivesController,
                        uint8 aTokenDecimals,
                        string calldata aTokenName,
                        string calldata aTokenSymbol,
                        bytes calldata params
                      ) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                    /**
                     * @title IPool
                     * @author Aave
                     * @notice Defines the basic interface for an Aave Pool.
                     */
                    interface IPool {
                      /**
                       * @dev Emitted on mintUnbacked()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param user The address initiating the supply
                       * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens
                       * @param amount The amount of supplied assets
                       * @param referralCode The referral code used
                       */
                      event MintUnbacked(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted on backUnbacked()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param backer The address paying for the backing
                       * @param amount The amount added as backing
                       * @param fee The amount paid in fees
                       */
                      event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
                      /**
                       * @dev Emitted on supply()
                       * @param reserve The address of the underlying asset of the reserve
                       * @param user The address initiating the supply
                       * @param onBehalfOf The beneficiary of the supply, receiving the aTokens
                       * @param amount The amount supplied
                       * @param referralCode The referral code used
                       */
                      event Supply(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted on withdraw()
                       * @param reserve The address of the underlying asset being withdrawn
                       * @param user The address initiating the withdrawal, owner of aTokens
                       * @param to The 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 interestRateMode The rate mode: 1 for Stable, 2 for Variable
                       * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray
                       * @param referralCode The referral code used
                       */
                      event Borrow(
                        address indexed reserve,
                        address user,
                        address indexed onBehalfOf,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 borrowRate,
                        uint16 indexed referralCode
                      );
                      /**
                       * @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
                       * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
                       */
                      event Repay(
                        address indexed reserve,
                        address indexed user,
                        address indexed repayer,
                        uint256 amount,
                        bool useATokens
                      );
                      /**
                       * @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 interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable
                       */
                      event SwapBorrowRateMode(
                        address indexed reserve,
                        address indexed user,
                        DataTypes.InterestRateMode interestRateMode
                      );
                      /**
                       * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets
                       * @param asset The address of the underlying asset of the reserve
                       * @param totalDebt The total isolation mode debt for the reserve
                       */
                      event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                      /**
                       * @dev Emitted when the user selects a certain asset category for eMode
                       * @param user The address of the user
                       * @param categoryId The category id
                       */
                      event UserEModeSet(address indexed user, uint8 categoryId);
                      /**
                       * @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 interestRateMode The flashloan mode: 0 for regular flashloan, 1 for Stable debt, 2 for Variable debt
                       * @param premium The fee flash borrowed
                       * @param referralCode The referral code used
                       */
                      event FlashLoan(
                        address indexed target,
                        address initiator,
                        address indexed asset,
                        uint256 amount,
                        DataTypes.InterestRateMode interestRateMode,
                        uint256 premium,
                        uint16 indexed referralCode
                      );
                      /**
                       * @dev Emitted when a borrower is liquidated.
                       * @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 liquidator
                       * @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.
                       * @param reserve The address of the underlying asset of the reserve
                       * @param liquidityRate The next liquidity rate
                       * @param stableBorrowRate The next stable borrow rate
                       * @param variableBorrowRate The next variable borrow rate
                       * @param liquidityIndex The next liquidity index
                       * @param variableBorrowIndex The next variable borrow index
                       */
                      event ReserveDataUpdated(
                        address indexed reserve,
                        uint256 liquidityRate,
                        uint256 stableBorrowRate,
                        uint256 variableBorrowRate,
                        uint256 liquidityIndex,
                        uint256 variableBorrowIndex
                      );
                      /**
                       * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.
                       * @param reserve The address of the reserve
                       * @param amountMinted The amount minted to the treasury
                       */
                      event MintedToTreasury(address indexed reserve, uint256 amountMinted);
                      /**
                       * @notice Mints an `amount` of aTokens to the `onBehalfOf`
                       * @param asset The address of the underlying asset to mint
                       * @param amount The amount to mint
                       * @param onBehalfOf The address that will receive the aTokens
                       * @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 mintUnbacked(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Back the current unbacked underlying with `amount` and pay `fee`.
                       * @param asset The address of the underlying asset to back
                       * @param amount The amount to back
                       * @param fee The amount paid in fees
                       * @return The backed amount
                       */
                      function backUnbacked(
                        address asset,
                        uint256 amount,
                        uint256 fee
                      ) external returns (uint256);
                      /**
                       * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                       * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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 supply(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Supply with transfer approval of asset to be supplied done via permit function
                       * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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 deadline The deadline timestamp that the permit is valid
                       * @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 permitV The V parameter of ERC712 permit sig
                       * @param permitR The R parameter of ERC712 permit sig
                       * @param permitS The S parameter of ERC712 permit sig
                       */
                      function supplyWithPermit(
                        address asset,
                        uint256 amount,
                        address onBehalfOf,
                        uint16 referralCode,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) external;
                      /**
                       * @notice 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 The 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);
                      /**
                       * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                       * already supplied 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 The 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 The 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 interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                       * @param onBehalfOf The 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 interestRateMode,
                        address onBehalfOf
                      ) external returns (uint256);
                      /**
                       * @notice Repay with transfer approval of asset to be repaid done via permit function
                       * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
                       * @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 interestRateMode 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
                       * @param deadline The deadline timestamp that the permit is valid
                       * @param permitV The V parameter of ERC712 permit sig
                       * @param permitR The R parameter of ERC712 permit sig
                       * @param permitS The S parameter of ERC712 permit sig
                       * @return The final amount repaid
                       */
                      function repayWithPermit(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode,
                        address onBehalfOf,
                        uint256 deadline,
                        uint8 permitV,
                        bytes32 permitR,
                        bytes32 permitS
                      ) external returns (uint256);
                      /**
                       * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
                       * equivalent debt tokens
                       * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable/stable debt tokens
                       * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
                       * balance is not enough to cover the whole debt
                       * @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 interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                       * @return The final amount repaid
                       */
                      function repayWithATokens(
                        address asset,
                        uint256 amount,
                        uint256 interestRateMode
                      ) external returns (uint256);
                      /**
                       * @notice Allows a borrower to swap his debt between stable and variable mode, or vice versa
                       * @param asset The address of the underlying asset borrowed
                       * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable
                       */
                      function swapBorrowRateMode(address asset, uint256 interestRateMode) external;
                      /**
                       * @notice 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 supply APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too
                       *        much has been borrowed at a stable rate and suppliers 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;
                      /**
                       * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
                       * @param asset The address of the underlying asset supplied
                       * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
                       */
                      function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                      /**
                       * @notice 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;
                      /**
                       * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                       * as long as the amount taken plus a fee is returned.
                       * @dev 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 IFlashLoanReceiver interface
                       * @param assets The addresses of the assets being flash-borrowed
                       * @param amounts The amounts of the assets being flash-borrowed
                       * @param interestRateModes 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 The 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 interestRateModes,
                        address onBehalfOf,
                        bytes calldata params,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
                       * as long as the amount taken plus a fee is returned.
                       * @dev 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 IFlashLoanSimpleReceiver interface
                       * @param asset The address of the asset being flash-borrowed
                       * @param amount The amount of the asset being flash-borrowed
                       * @param params Variadic packed params to pass to the receiver as extra information
                       * @param referralCode The 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 flashLoanSimple(
                        address receiverAddress,
                        address asset,
                        uint256 amount,
                        bytes calldata params,
                        uint16 referralCode
                      ) external;
                      /**
                       * @notice Returns the user account data across all the reserves
                       * @param user The address of the user
                       * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
                       * @return totalDebtBase The total debt of the user in the base currency used by the price feed
                       * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
                       * @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 totalCollateralBase,
                          uint256 totalDebtBase,
                          uint256 availableBorrowsBase,
                          uint256 currentLiquidationThreshold,
                          uint256 ltv,
                          uint256 healthFactor
                        );
                      /**
                       * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                       * interest rate strategy
                       * @dev Only callable by the PoolConfigurator 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 variableDebtAddress 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;
                      /**
                       * @notice Drop a reserve
                       * @dev Only callable by the PoolConfigurator contract
                       * @param asset The address of the underlying asset of the reserve
                       */
                      function dropReserve(address asset) external;
                      /**
                       * @notice Updates the address of the interest rate strategy contract
                       * @dev Only callable by the PoolConfigurator 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;
                      /**
                       * @notice Sets the configuration bitmap of the reserve as a whole
                       * @dev Only callable by the PoolConfigurator contract
                       * @param asset The address of the underlying asset of the reserve
                       * @param configuration The new configuration bitmap
                       */
                      function setConfiguration(address asset, DataTypes.ReserveConfigurationMap calldata configuration)
                        external;
                      /**
                       * @notice 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);
                      /**
                       * @notice 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);
                      /**
                       * @notice Returns the 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);
                      /**
                       * @notice Returns the normalized variable debt per unit of asset
                       * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a
                       * "dynamic" variable index based on time, current stored index and virtual rate at the current
                       * moment (approx. a borrower would get if opening a position). This means that is always used in
                       * combination with variable debt supply/balances.
                       * If using this function externally, consider that is possible to have an increasing normalized
                       * variable debt that is not equivalent to how the variable debt index would be updated in storage
                       * (e.g. only updates with non-zero variable debt supply)
                       * @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);
                      /**
                       * @notice Returns the state and configuration of the reserve
                       * @param asset The address of the underlying asset of the reserve
                       * @return The state and configuration data of the reserve
                       */
                      function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);
                      /**
                       * @notice Validates and finalizes an aToken transfer
                       * @dev 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;
                      /**
                       * @notice Returns the list of the underlying assets of all the initialized reserves
                       * @dev It does not include dropped reserves
                       * @return The addresses of the underlying assets of the initialized reserves
                       */
                      function getReservesList() external view returns (address[] memory);
                      /**
                       * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct
                       * @param id The id of the reserve as stored in the DataTypes.ReserveData struct
                       * @return The address of the reserve associated with id
                       */
                      function getReserveAddressById(uint16 id) external view returns (address);
                      /**
                       * @notice Returns the PoolAddressesProvider connected to this contract
                       * @return The address of the PoolAddressesProvider
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Updates the protocol fee on the bridging
                       * @param bridgeProtocolFee The part of the premium sent to the protocol treasury
                       */
                      function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;
                      /**
                       * @notice Updates flash loan premiums. Flash loan premium consists of two parts:
                       * - A part is sent to aToken holders as extra, one time accumulated interest
                       * - A part is collected by the protocol treasury
                       * @dev The total premium is calculated on the total borrowed amount
                       * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`
                       * @dev Only callable by the PoolConfigurator contract
                       * @param flashLoanPremiumTotal The total premium, expressed in bps
                       * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps
                       */
                      function updateFlashloanPremiums(
                        uint128 flashLoanPremiumTotal,
                        uint128 flashLoanPremiumToProtocol
                      ) external;
                      /**
                       * @notice Configures a new category for the eMode.
                       * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.
                       * The category 0 is reserved as it's the default for volatile assets
                       * @param id The id of the category
                       * @param config The configuration of the category
                       */
                      function configureEModeCategory(uint8 id, DataTypes.EModeCategory memory config) external;
                      /**
                       * @notice Returns the data of an eMode category
                       * @param id The id of the category
                       * @return The configuration data of the category
                       */
                      function getEModeCategoryData(uint8 id) external view returns (DataTypes.EModeCategory memory);
                      /**
                       * @notice Allows a user to use the protocol in eMode
                       * @param categoryId The id of the category
                       */
                      function setUserEMode(uint8 categoryId) external;
                      /**
                       * @notice Returns the eMode the user is using
                       * @param user The address of the user
                       * @return The eMode id
                       */
                      function getUserEMode(address user) external view returns (uint256);
                      /**
                       * @notice Resets the isolation mode total debt of the given asset to zero
                       * @dev It requires the given asset has zero debt ceiling
                       * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
                       */
                      function resetIsolationModeTotalDebt(address asset) external;
                      /**
                       * @notice Returns the percentage of available liquidity that can be borrowed at once at stable rate
                       * @return The percentage of available liquidity to borrow, expressed in bps
                       */
                      function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() external view returns (uint256);
                      /**
                       * @notice Returns the total fee on flash loans
                       * @return The total fee on flashloans
                       */
                      function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);
                      /**
                       * @notice Returns the part of the bridge fees sent to protocol
                       * @return The bridge fee sent to the protocol treasury
                       */
                      function BRIDGE_PROTOCOL_FEE() external view returns (uint256);
                      /**
                       * @notice Returns the part of the flashloan fees sent to protocol
                       * @return The flashloan fee sent to the protocol treasury
                       */
                      function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);
                      /**
                       * @notice Returns the maximum number of reserves supported to be listed in this Pool
                       * @return The maximum number of reserves supported
                       */
                      function MAX_NUMBER_RESERVES() external view returns (uint16);
                      /**
                       * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
                       * @param assets The list of reserves for which the minting needs to be executed
                       */
                      function mintToTreasury(address[] calldata assets) external;
                      /**
                       * @notice Rescue and transfer tokens locked in this contract
                       * @param token The address of the token
                       * @param to The address of the recipient
                       * @param amount The amount of token to transfer
                       */
                      function rescueTokens(
                        address token,
                        address to,
                        uint256 amount
                      ) external;
                      /**
                       * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                       * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
                       * @dev Deprecated: Use the `supply` function instead
                       * @param asset The address of the underlying asset to supply
                       * @param amount The amount to be supplied
                       * @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;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    /**
                     * @title IPoolAddressesProvider
                     * @author Aave
                     * @notice Defines the basic interface for a Pool Addresses Provider.
                     */
                    interface IPoolAddressesProvider {
                      /**
                       * @dev Emitted when the market identifier is updated.
                       * @param oldMarketId The old id of the market
                       * @param newMarketId The new id of the market
                       */
                      event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                      /**
                       * @dev Emitted when the pool is updated.
                       * @param oldAddress The old address of the Pool
                       * @param newAddress The new address of the Pool
                       */
                      event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool configurator is updated.
                       * @param oldAddress The old address of the PoolConfigurator
                       * @param newAddress The new address of the PoolConfigurator
                       */
                      event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle is updated.
                       * @param oldAddress The old address of the PriceOracle
                       * @param newAddress The new address of the PriceOracle
                       */
                      event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL manager is updated.
                       * @param oldAddress The old address of the ACLManager
                       * @param newAddress The new address of the ACLManager
                       */
                      event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL admin is updated.
                       * @param oldAddress The old address of the ACLAdmin
                       * @param newAddress The new address of the ACLAdmin
                       */
                      event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle sentinel is updated.
                       * @param oldAddress The old address of the PriceOracleSentinel
                       * @param newAddress The new address of the PriceOracleSentinel
                       */
                      event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool data provider is updated.
                       * @param oldAddress The old address of the PoolDataProvider
                       * @param newAddress The new address of the PoolDataProvider
                       */
                      event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when a new proxy is created.
                       * @param id The identifier of the proxy
                       * @param proxyAddress The address of the created proxy contract
                       * @param implementationAddress The address of the implementation contract
                       */
                      event ProxyCreated(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address indexed implementationAddress
                      );
                      /**
                       * @dev Emitted when a new non-proxied contract address is registered.
                       * @param id The identifier of the contract
                       * @param oldAddress The address of the old contract
                       * @param newAddress The address of the new contract
                       */
                      event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the implementation of the proxy registered with id is updated
                       * @param id The identifier of the contract
                       * @param proxyAddress The address of the proxy contract
                       * @param oldImplementationAddress The address of the old implementation contract
                       * @param newImplementationAddress The address of the new implementation contract
                       */
                      event AddressSetAsProxy(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address oldImplementationAddress,
                        address indexed newImplementationAddress
                      );
                      /**
                       * @notice Returns the id of the Aave market to which this contract points to.
                       * @return The market id
                       */
                      function getMarketId() external view returns (string memory);
                      /**
                       * @notice Associates an id with a specific PoolAddressesProvider.
                       * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                       * identify and validate multiple Aave markets.
                       * @param newMarketId The market id
                       */
                      function setMarketId(string calldata newMarketId) external;
                      /**
                       * @notice Returns an address by its identifier.
                       * @dev The returned address might be an EOA or a contract, potentially proxied
                       * @dev It returns ZERO if there is no registered address with the given id
                       * @param id The id
                       * @return The address of the registered for the specified id
                       */
                      function getAddress(bytes32 id) external view returns (address);
                      /**
                       * @notice 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 `newImplementationAddress`.
                       * @dev 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 newImplementationAddress The address of the new implementation
                       */
                      function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                      /**
                       * @notice Sets an address for an id replacing the address saved in the addresses map.
                       * @dev 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;
                      /**
                       * @notice Returns the address of the Pool proxy.
                       * @return The Pool proxy address
                       */
                      function getPool() external view returns (address);
                      /**
                       * @notice Updates the implementation of the Pool, or creates a proxy
                       * setting the new `pool` implementation when the function is called for the first time.
                       * @param newPoolImpl The new Pool implementation
                       */
                      function setPoolImpl(address newPoolImpl) external;
                      /**
                       * @notice Returns the address of the PoolConfigurator proxy.
                       * @return The PoolConfigurator proxy address
                       */
                      function getPoolConfigurator() external view returns (address);
                      /**
                       * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                       * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                       * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                       */
                      function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                      /**
                       * @notice Returns the address of the price oracle.
                       * @return The address of the PriceOracle
                       */
                      function getPriceOracle() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle.
                       * @param newPriceOracle The address of the new PriceOracle
                       */
                      function setPriceOracle(address newPriceOracle) external;
                      /**
                       * @notice Returns the address of the ACL manager.
                       * @return The address of the ACLManager
                       */
                      function getACLManager() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL manager.
                       * @param newAclManager The address of the new ACLManager
                       */
                      function setACLManager(address newAclManager) external;
                      /**
                       * @notice Returns the address of the ACL admin.
                       * @return The address of the ACL admin
                       */
                      function getACLAdmin() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL admin.
                       * @param newAclAdmin The address of the new ACL admin
                       */
                      function setACLAdmin(address newAclAdmin) external;
                      /**
                       * @notice Returns the address of the price oracle sentinel.
                       * @return The address of the PriceOracleSentinel
                       */
                      function getPriceOracleSentinel() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle sentinel.
                       * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                       */
                      function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                      /**
                       * @notice Returns the address of the data provider.
                       * @return The address of the DataProvider
                       */
                      function getPoolDataProvider() external view returns (address);
                      /**
                       * @notice Updates the address of the data provider.
                       * @param newDataProvider The address of the new DataProvider
                       */
                      function setPoolDataProvider(address newDataProvider) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    /**
                     * @title IScaledBalanceToken
                     * @author Aave
                     * @notice Defines the basic interface for a scaled-balance token.
                     */
                    interface IScaledBalanceToken {
                      /**
                       * @dev Emitted after the mint action
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the minted tokens
                       * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
                       * @param index The next liquidity index of the reserve
                       */
                      event Mint(
                        address indexed caller,
                        address indexed onBehalfOf,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @dev Emitted after the burn action
                       * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
                       * @param from The address from which the tokens will be burned
                       * @param target The address that will receive the underlying, if any
                       * @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
                       * @param index The next liquidity index of the reserve
                       */
                      event Burn(
                        address indexed from,
                        address indexed target,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @notice Returns the scaled balance of the user.
                       * @dev 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);
                      /**
                       * @notice 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 total supply
                       */
                      function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                      /**
                       * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
                       * @return The scaled total supply
                       */
                      function scaledTotalSupply() external view returns (uint256);
                      /**
                       * @notice Returns last index interest was accrued to the user's balance
                       * @param user The address of the user
                       * @return The last index interest was accrued to the user's balance, expressed in ray
                       */
                      function getPreviousIndex(address user) external view returns (uint256);
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    /**
                     * @title VersionedInitializable
                     * @author Aave, inspired by the OpenZeppelin Initializable contract
                     * @notice Helper contract to implement initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * @dev 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.
                     */
                    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;
                        }
                      }
                      /**
                       * @notice Returns the revision number of the contract
                       * @dev Needs to be defined in the inherited class as a constant.
                       * @return The revision number
                       */
                      function getRevision() internal pure virtual returns (uint256);
                      /**
                       * @notice Returns true if and only if the function is running in the constructor
                       * @return True 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: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @title Errors library
                     * @author Aave
                     * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                     */
                    library Errors {
                      string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                      string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                      string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                      string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                      string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                      string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                      string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                      string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                      string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                      string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                      string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                      string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                      string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                      string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                      string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                      string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                      string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                      string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                      string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                      string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                      string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                      string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                      string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                      string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                      string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                      string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                      string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                      string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                      string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                      string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                      string public constant STABLE_BORROWING_NOT_ENABLED = '31'; // 'Stable borrowing is not enabled'
                      string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                      string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                      string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                      string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                      string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                      string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                      string public constant AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '38'; // 'The requested amount is greater than the max loan size in stable rate mode'
                      string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                      string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                      string public constant NO_OUTSTANDING_STABLE_DEBT = '41'; // 'User does not have outstanding stable rate debt on this reserve'
                      string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                      string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                      string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                      string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                      string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                      string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                      string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                      string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                      string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                      string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                      string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                      string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                      string public constant STABLE_DEBT_NOT_ZERO = '55'; // 'Stable debt supply is not zero'
                      string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                      string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                      string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                      string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                      string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                      string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                      string public constant USER_IN_ISOLATION_MODE = '62'; // 'User is in isolation mode'
                      string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                      string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                      string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                      string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                      string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                      string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                      string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                      string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                      string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                      string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                      string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                      string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                      string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                      string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                      string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                      string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                      string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                      string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                      string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                      string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                      string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                      string public constant INVALID_OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO = '84'; // 'Invalid optimal stable to total debt ratio'
                      string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                      string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                      string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                      string public constant STABLE_BORROWING_ENABLED = '88'; // 'Stable borrowing is enabled'
                      string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                      string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                      string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @title WadRayMath library
                     * @author Aave
                     * @notice Provides functions to perform calculations with Wad and Ray units
                     * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                     * with 27 digits of precision)
                     * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                     */
                    library WadRayMath {
                      // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                      uint256 internal constant WAD = 1e18;
                      uint256 internal constant HALF_WAD = 0.5e18;
                      uint256 internal constant RAY = 1e27;
                      uint256 internal constant HALF_RAY = 0.5e27;
                      uint256 internal constant WAD_RAY_RATIO = 1e9;
                      /**
                       * @dev Multiplies two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a*b, in wad
                       */
                      function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_WAD), WAD)
                        }
                      }
                      /**
                       * @dev Divides two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a/b, in wad
                       */
                      function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, WAD), div(b, 2)), b)
                        }
                      }
                      /**
                       * @notice Multiplies two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raymul b
                       */
                      function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_RAY), RAY)
                        }
                      }
                      /**
                       * @notice Divides two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raydiv b
                       */
                      function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, RAY), div(b, 2)), b)
                        }
                      }
                      /**
                       * @dev Casts ray down to wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @return b = a converted to wad, rounded half up to the nearest wad
                       */
                      function rayToWad(uint256 a) internal pure returns (uint256 b) {
                        assembly {
                          b := div(a, WAD_RAY_RATIO)
                          let remainder := mod(a, WAD_RAY_RATIO)
                          if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                            b := add(b, 1)
                          }
                        }
                      }
                      /**
                       * @dev Converts wad up to ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @return b = a converted in ray
                       */
                      function wadToRay(uint256 a) internal pure returns (uint256 b) {
                        // to avoid overflow, b/WAD_RAY_RATIO == a
                        assembly {
                          b := mul(a, WAD_RAY_RATIO)
                          if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                            revert(0, 0)
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    library DataTypes {
                      struct ReserveData {
                        //stores the reserve configuration
                        ReserveConfigurationMap configuration;
                        //the liquidity index. Expressed in ray
                        uint128 liquidityIndex;
                        //the current supply rate. Expressed in ray
                        uint128 currentLiquidityRate;
                        //variable borrow index. Expressed in ray
                        uint128 variableBorrowIndex;
                        //the current variable borrow rate. Expressed in ray
                        uint128 currentVariableBorrowRate;
                        //the current stable borrow rate. Expressed in ray
                        uint128 currentStableBorrowRate;
                        //timestamp of last update
                        uint40 lastUpdateTimestamp;
                        //the id of the reserve. Represents the position in the list of the active reserves
                        uint16 id;
                        //aToken address
                        address aTokenAddress;
                        //stableDebtToken address
                        address stableDebtTokenAddress;
                        //variableDebtToken address
                        address variableDebtTokenAddress;
                        //address of the interest rate strategy
                        address interestRateStrategyAddress;
                        //the current treasury balance, scaled
                        uint128 accruedToTreasury;
                        //the outstanding unbacked aTokens minted through the bridging feature
                        uint128 unbacked;
                        //the outstanding debt borrowed against this asset in isolation mode
                        uint128 isolationModeTotalDebt;
                      }
                      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: asset is paused
                        //bit 61: borrowing in isolation mode is enabled
                        //bit 62-63: reserved
                        //bit 64-79: reserve factor
                        //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap
                        //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap
                        //bit 152-167 liquidation protocol fee
                        //bit 168-175 eMode category
                        //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                        //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                        //bit 252-255 unused
                        uint256 data;
                      }
                      struct UserConfigurationMap {
                        /**
                         * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                         * The first bit indicates if an asset is used as collateral by the user, the second whether an
                         * asset is borrowed by the user.
                         */
                        uint256 data;
                      }
                      struct EModeCategory {
                        // each eMode category has a custom ltv and liquidation threshold
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        // each eMode category may or may not have a custom oracle to override the individual assets price oracles
                        address priceSource;
                        string label;
                      }
                      enum InterestRateMode {
                        NONE,
                        STABLE,
                        VARIABLE
                      }
                      struct ReserveCache {
                        uint256 currScaledVariableDebt;
                        uint256 nextScaledVariableDebt;
                        uint256 currPrincipalStableDebt;
                        uint256 currAvgStableBorrowRate;
                        uint256 currTotalStableDebt;
                        uint256 nextAvgStableBorrowRate;
                        uint256 nextTotalStableDebt;
                        uint256 currLiquidityIndex;
                        uint256 nextLiquidityIndex;
                        uint256 currVariableBorrowIndex;
                        uint256 nextVariableBorrowIndex;
                        uint256 currLiquidityRate;
                        uint256 currVariableBorrowRate;
                        uint256 reserveFactor;
                        ReserveConfigurationMap reserveConfiguration;
                        address aTokenAddress;
                        address stableDebtTokenAddress;
                        address variableDebtTokenAddress;
                        uint40 reserveLastUpdateTimestamp;
                        uint40 stableDebtLastUpdateTimestamp;
                      }
                      struct ExecuteLiquidationCallParams {
                        uint256 reservesCount;
                        uint256 debtToCover;
                        address collateralAsset;
                        address debtAsset;
                        address user;
                        bool receiveAToken;
                        address priceOracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteSupplyParams {
                        address asset;
                        uint256 amount;
                        address onBehalfOf;
                        uint16 referralCode;
                      }
                      struct ExecuteBorrowParams {
                        address asset;
                        address user;
                        address onBehalfOf;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint16 referralCode;
                        bool releaseUnderlying;
                        uint256 maxStableRateBorrowSizePercent;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteRepayParams {
                        address asset;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        address onBehalfOf;
                        bool useATokens;
                      }
                      struct ExecuteWithdrawParams {
                        address asset;
                        uint256 amount;
                        address to;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ExecuteSetUserEModeParams {
                        uint256 reservesCount;
                        address oracle;
                        uint8 categoryId;
                      }
                      struct FinalizeTransferParams {
                        address asset;
                        address from;
                        address to;
                        uint256 amount;
                        uint256 balanceFromBefore;
                        uint256 balanceToBefore;
                        uint256 reservesCount;
                        address oracle;
                        uint8 fromEModeCategory;
                      }
                      struct FlashloanParams {
                        address receiverAddress;
                        address[] assets;
                        uint256[] amounts;
                        uint256[] interestRateModes;
                        address onBehalfOf;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                        uint256 maxStableRateBorrowSizePercent;
                        uint256 reservesCount;
                        address addressesProvider;
                        uint8 userEModeCategory;
                        bool isAuthorizedFlashBorrower;
                      }
                      struct FlashloanSimpleParams {
                        address receiverAddress;
                        address asset;
                        uint256 amount;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                      }
                      struct FlashLoanRepaymentParams {
                        uint256 amount;
                        uint256 totalPremium;
                        uint256 flashLoanPremiumToProtocol;
                        address asset;
                        address receiverAddress;
                        uint16 referralCode;
                      }
                      struct CalculateUserAccountDataParams {
                        UserConfigurationMap userConfig;
                        uint256 reservesCount;
                        address user;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ValidateBorrowParams {
                        ReserveCache reserveCache;
                        UserConfigurationMap userConfig;
                        address asset;
                        address userAddress;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint256 maxStableLoanPercent;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                        bool isolationModeActive;
                        address isolationModeCollateralAddress;
                        uint256 isolationModeDebtCeiling;
                      }
                      struct ValidateLiquidationCallParams {
                        ReserveCache debtReserveCache;
                        uint256 totalDebt;
                        uint256 healthFactor;
                        address priceOracleSentinel;
                      }
                      struct CalculateInterestRatesParams {
                        uint256 unbacked;
                        uint256 liquidityAdded;
                        uint256 liquidityTaken;
                        uint256 totalStableDebt;
                        uint256 totalVariableDebt;
                        uint256 averageStableBorrowRate;
                        uint256 reserveFactor;
                        address reserve;
                        address aToken;
                      }
                      struct InitReserveParams {
                        address asset;
                        address aTokenAddress;
                        address stableDebtAddress;
                        address variableDebtAddress;
                        address interestRateStrategyAddress;
                        uint16 reservesCount;
                        uint16 maxNumberReserves;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {GPv2SafeERC20} from '../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                    import {SafeCast} from '../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
                    import {Errors} from '../libraries/helpers/Errors.sol';
                    import {WadRayMath} from '../libraries/math/WadRayMath.sol';
                    import {IPool} from '../../interfaces/IPool.sol';
                    import {IAToken} from '../../interfaces/IAToken.sol';
                    import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
                    import {IInitializableAToken} from '../../interfaces/IInitializableAToken.sol';
                    import {ScaledBalanceTokenBase} from './base/ScaledBalanceTokenBase.sol';
                    import {IncentivizedERC20} from './base/IncentivizedERC20.sol';
                    import {EIP712Base} from './base/EIP712Base.sol';
                    /**
                     * @title Aave ERC20 AToken
                     * @author Aave
                     * @notice Implementation of the interest bearing token for the Aave protocol
                     */
                    contract AToken is VersionedInitializable, ScaledBalanceTokenBase, EIP712Base, IAToken {
                      using WadRayMath for uint256;
                      using SafeCast for uint256;
                      using GPv2SafeERC20 for IERC20;
                      bytes32 public constant PERMIT_TYPEHASH =
                        keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
                      uint256 public constant ATOKEN_REVISION = 0x1;
                      address internal _treasury;
                      address internal _underlyingAsset;
                      /// @inheritdoc VersionedInitializable
                      function getRevision() internal pure virtual override returns (uint256) {
                        return ATOKEN_REVISION;
                      }
                      /**
                       * @dev Constructor.
                       * @param pool The address of the Pool contract
                       */
                      constructor(IPool pool)
                        ScaledBalanceTokenBase(pool, 'ATOKEN_IMPL', 'ATOKEN_IMPL', 0)
                        EIP712Base()
                      {
                        // Intentionally left blank
                      }
                      /// @inheritdoc IInitializableAToken
                      function initialize(
                        IPool initializingPool,
                        address treasury,
                        address underlyingAsset,
                        IAaveIncentivesController incentivesController,
                        uint8 aTokenDecimals,
                        string calldata aTokenName,
                        string calldata aTokenSymbol,
                        bytes calldata params
                      ) public virtual override initializer {
                        require(initializingPool == POOL, Errors.POOL_ADDRESSES_DO_NOT_MATCH);
                        _setName(aTokenName);
                        _setSymbol(aTokenSymbol);
                        _setDecimals(aTokenDecimals);
                        _treasury = treasury;
                        _underlyingAsset = underlyingAsset;
                        _incentivesController = incentivesController;
                        _domainSeparator = _calculateDomainSeparator();
                        emit Initialized(
                          underlyingAsset,
                          address(POOL),
                          treasury,
                          address(incentivesController),
                          aTokenDecimals,
                          aTokenName,
                          aTokenSymbol,
                          params
                        );
                      }
                      /// @inheritdoc IAToken
                      function mint(
                        address caller,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) external virtual override onlyPool returns (bool) {
                        return _mintScaled(caller, onBehalfOf, amount, index);
                      }
                      /// @inheritdoc IAToken
                      function burn(
                        address from,
                        address receiverOfUnderlying,
                        uint256 amount,
                        uint256 index
                      ) external virtual override onlyPool {
                        _burnScaled(from, receiverOfUnderlying, amount, index);
                        if (receiverOfUnderlying != address(this)) {
                          IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount);
                        }
                      }
                      /// @inheritdoc IAToken
                      function mintToTreasury(uint256 amount, uint256 index) external virtual override onlyPool {
                        if (amount == 0) {
                          return;
                        }
                        _mintScaled(address(POOL), _treasury, amount, index);
                      }
                      /// @inheritdoc IAToken
                      function transferOnLiquidation(
                        address from,
                        address to,
                        uint256 value
                      ) external virtual override onlyPool {
                        // Being a normal transfer, the Transfer() and BalanceTransfer() are emitted
                        // so no need to emit a specific event here
                        _transfer(from, to, value, false);
                      }
                      /// @inheritdoc IERC20
                      function balanceOf(address user)
                        public
                        view
                        virtual
                        override(IncentivizedERC20, IERC20)
                        returns (uint256)
                      {
                        return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));
                      }
                      /// @inheritdoc IERC20
                      function totalSupply() public view virtual override(IncentivizedERC20, IERC20) returns (uint256) {
                        uint256 currentSupplyScaled = super.totalSupply();
                        if (currentSupplyScaled == 0) {
                          return 0;
                        }
                        return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));
                      }
                      /// @inheritdoc IAToken
                      function RESERVE_TREASURY_ADDRESS() external view override returns (address) {
                        return _treasury;
                      }
                      /// @inheritdoc IAToken
                      function UNDERLYING_ASSET_ADDRESS() external view override returns (address) {
                        return _underlyingAsset;
                      }
                      /// @inheritdoc IAToken
                      function transferUnderlyingTo(address target, uint256 amount) external virtual override onlyPool {
                        IERC20(_underlyingAsset).safeTransfer(target, amount);
                      }
                      /// @inheritdoc IAToken
                      function handleRepayment(
                        address user,
                        address onBehalfOf,
                        uint256 amount
                      ) external virtual override onlyPool {
                        // Intentionally left blank
                      }
                      /// @inheritdoc IAToken
                      function permit(
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                      ) external override {
                        require(owner != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                        //solium-disable-next-line
                        require(block.timestamp <= deadline, Errors.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), Errors.INVALID_SIGNATURE);
                        _nonces[owner] = currentValidNonce + 1;
                        _approve(owner, spender, value);
                      }
                      /**
                       * @notice 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, false otherwise
                       */
                      function _transfer(
                        address from,
                        address to,
                        uint256 amount,
                        bool validate
                      ) internal virtual {
                        address underlyingAsset = _underlyingAsset;
                        uint256 index = POOL.getReserveNormalizedIncome(underlyingAsset);
                        uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index);
                        uint256 toBalanceBefore = super.balanceOf(to).rayMul(index);
                        super._transfer(from, to, amount, index);
                        if (validate) {
                          POOL.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore);
                        }
                        emit BalanceTransfer(from, to, amount.rayDiv(index), index);
                      }
                      /**
                       * @notice 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,
                        uint128 amount
                      ) internal virtual override {
                        _transfer(from, to, amount, true);
                      }
                      /**
                       * @dev Overrides the base function to fully implement IAToken
                       * @dev see `EIP712Base.DOMAIN_SEPARATOR()` for more detailed documentation
                       */
                      function DOMAIN_SEPARATOR() public view override(IAToken, EIP712Base) returns (bytes32) {
                        return super.DOMAIN_SEPARATOR();
                      }
                      /**
                       * @dev Overrides the base function to fully implement IAToken
                       * @dev see `EIP712Base.nonces()` for more detailed documentation
                       */
                      function nonces(address owner) public view override(IAToken, EIP712Base) returns (uint256) {
                        return super.nonces(owner);
                      }
                      /// @inheritdoc EIP712Base
                      function _EIP712BaseId() internal view override returns (string memory) {
                        return name();
                      }
                      /// @inheritdoc IAToken
                      function rescueTokens(
                        address token,
                        address to,
                        uint256 amount
                      ) external override onlyPoolAdmin {
                        require(token != _underlyingAsset, Errors.UNDERLYING_CANNOT_BE_RESCUED);
                        IERC20(token).safeTransfer(to, amount);
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    /**
                     * @title EIP712Base
                     * @author Aave
                     * @notice Base contract implementation of EIP712.
                     */
                    abstract contract EIP712Base {
                      bytes public constant EIP712_REVISION = bytes('1');
                      bytes32 internal constant EIP712_DOMAIN =
                        keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
                      // Map of address nonces (address => nonce)
                      mapping(address => uint256) internal _nonces;
                      bytes32 internal _domainSeparator;
                      uint256 internal immutable _chainId;
                      /**
                       * @dev Constructor.
                       */
                      constructor() {
                        _chainId = block.chainid;
                      }
                      /**
                       * @notice Get the domain separator for the token
                       * @dev Return cached value if chainId matches cache, otherwise recomputes separator
                       * @return The domain separator of the token at current chain
                       */
                      function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                        if (block.chainid == _chainId) {
                          return _domainSeparator;
                        }
                        return _calculateDomainSeparator();
                      }
                      /**
                       * @notice Returns the nonce value for address specified as parameter
                       * @param owner The address for which the nonce is being returned
                       * @return The nonce value for the input address`
                       */
                      function nonces(address owner) public view virtual returns (uint256) {
                        return _nonces[owner];
                      }
                      /**
                       * @notice Compute the current domain separator
                       * @return The domain separator for the token
                       */
                      function _calculateDomainSeparator() internal view returns (bytes32) {
                        return
                          keccak256(
                            abi.encode(
                              EIP712_DOMAIN,
                              keccak256(bytes(_EIP712BaseId())),
                              keccak256(EIP712_REVISION),
                              block.chainid,
                              address(this)
                            )
                          );
                      }
                      /**
                       * @notice Returns the user readable name of signing domain (e.g. token name)
                       * @return The name of the signing domain
                       */
                      function _EIP712BaseId() internal view virtual returns (string memory);
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    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 {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                    import {Errors} from '../../libraries/helpers/Errors.sol';
                    import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                    import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IACLManager} from '../../../interfaces/IACLManager.sol';
                    /**
                     * @title IncentivizedERC20
                     * @author Aave, inspired by the Openzeppelin ERC20 implementation
                     * @notice Basic ERC20 implementation
                     */
                    abstract contract IncentivizedERC20 is Context, IERC20Detailed {
                      using WadRayMath for uint256;
                      using SafeCast for uint256;
                      /**
                       * @dev Only pool admin can call functions marked by this modifier.
                       */
                      modifier onlyPoolAdmin() {
                        IACLManager aclManager = IACLManager(_addressesProvider.getACLManager());
                        require(aclManager.isPoolAdmin(msg.sender), Errors.CALLER_NOT_POOL_ADMIN);
                        _;
                      }
                      /**
                       * @dev Only pool can call functions marked by this modifier.
                       */
                      modifier onlyPool() {
                        require(_msgSender() == address(POOL), Errors.CALLER_MUST_BE_POOL);
                        _;
                      }
                      /**
                       * @dev UserState - additionalData is a flexible field.
                       * ATokens and VariableDebtTokens use this field store the index of the
                       * user's last supply/withdrawal/borrow/repayment. StableDebtTokens use
                       * this field to store the user's stable rate.
                       */
                      struct UserState {
                        uint128 balance;
                        uint128 additionalData;
                      }
                      // Map of users address and their state data (userAddress => userStateData)
                      mapping(address => UserState) internal _userState;
                      // Map of allowances (delegator => delegatee => allowanceAmount)
                      mapping(address => mapping(address => uint256)) private _allowances;
                      uint256 internal _totalSupply;
                      string private _name;
                      string private _symbol;
                      uint8 private _decimals;
                      IAaveIncentivesController internal _incentivesController;
                      IPoolAddressesProvider internal immutable _addressesProvider;
                      IPool public immutable POOL;
                      /**
                       * @dev Constructor.
                       * @param pool The reference to the main Pool contract
                       * @param name The name of the token
                       * @param symbol The symbol of the token
                       * @param decimals The number of decimals of the token
                       */
                      constructor(
                        IPool pool,
                        string memory name,
                        string memory symbol,
                        uint8 decimals
                      ) {
                        _addressesProvider = pool.ADDRESSES_PROVIDER();
                        _name = name;
                        _symbol = symbol;
                        _decimals = decimals;
                        POOL = pool;
                      }
                      /// @inheritdoc IERC20Detailed
                      function name() public view override returns (string memory) {
                        return _name;
                      }
                      /// @inheritdoc IERC20Detailed
                      function symbol() external view override returns (string memory) {
                        return _symbol;
                      }
                      /// @inheritdoc IERC20Detailed
                      function decimals() external view override returns (uint8) {
                        return _decimals;
                      }
                      /// @inheritdoc IERC20
                      function totalSupply() public view virtual override returns (uint256) {
                        return _totalSupply;
                      }
                      /// @inheritdoc IERC20
                      function balanceOf(address account) public view virtual override returns (uint256) {
                        return _userState[account].balance;
                      }
                      /**
                       * @notice Returns the address of the Incentives Controller contract
                       * @return The address of the Incentives Controller
                       */
                      function getIncentivesController() external view virtual returns (IAaveIncentivesController) {
                        return _incentivesController;
                      }
                      /**
                       * @notice Sets a new Incentives Controller
                       * @param controller the new Incentives controller
                       */
                      function setIncentivesController(IAaveIncentivesController controller) external onlyPoolAdmin {
                        _incentivesController = controller;
                      }
                      /// @inheritdoc IERC20
                      function transfer(address recipient, uint256 amount) external virtual override returns (bool) {
                        uint128 castAmount = amount.toUint128();
                        _transfer(_msgSender(), recipient, castAmount);
                        return true;
                      }
                      /// @inheritdoc IERC20
                      function allowance(address owner, address spender)
                        external
                        view
                        virtual
                        override
                        returns (uint256)
                      {
                        return _allowances[owner][spender];
                      }
                      /// @inheritdoc IERC20
                      function approve(address spender, uint256 amount) external virtual override returns (bool) {
                        _approve(_msgSender(), spender, amount);
                        return true;
                      }
                      /// @inheritdoc IERC20
                      function transferFrom(
                        address sender,
                        address recipient,
                        uint256 amount
                      ) external virtual override returns (bool) {
                        uint128 castAmount = amount.toUint128();
                        _approve(sender, _msgSender(), _allowances[sender][_msgSender()] - castAmount);
                        _transfer(sender, recipient, castAmount);
                        return true;
                      }
                      /**
                       * @notice 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) external virtual returns (bool) {
                        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                        return true;
                      }
                      /**
                       * @notice 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)
                        external
                        virtual
                        returns (bool)
                      {
                        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] - subtractedValue);
                        return true;
                      }
                      /**
                       * @notice Transfers tokens between two users and apply incentives if defined.
                       * @param sender The source address
                       * @param recipient The destination address
                       * @param amount The amount getting transferred
                       */
                      function _transfer(
                        address sender,
                        address recipient,
                        uint128 amount
                      ) internal virtual {
                        uint128 oldSenderBalance = _userState[sender].balance;
                        _userState[sender].balance = oldSenderBalance - amount;
                        uint128 oldRecipientBalance = _userState[recipient].balance;
                        _userState[recipient].balance = oldRecipientBalance + amount;
                        IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                        if (address(incentivesControllerLocal) != address(0)) {
                          uint256 currentTotalSupply = _totalSupply;
                          incentivesControllerLocal.handleAction(sender, currentTotalSupply, oldSenderBalance);
                          if (sender != recipient) {
                            incentivesControllerLocal.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                          }
                        }
                      }
                      /**
                       * @notice Approve `spender` to use `amount` of `owner`s balance
                       * @param owner The address owning the tokens
                       * @param spender The address approved for spending
                       * @param amount The amount of tokens to approve spending of
                       */
                      function _approve(
                        address owner,
                        address spender,
                        uint256 amount
                      ) internal virtual {
                        _allowances[owner][spender] = amount;
                        emit Approval(owner, spender, amount);
                      }
                      /**
                       * @notice Update the name of the token
                       * @param newName The new name for the token
                       */
                      function _setName(string memory newName) internal {
                        _name = newName;
                      }
                      /**
                       * @notice Update the symbol for the token
                       * @param newSymbol The new symbol for the token
                       */
                      function _setSymbol(string memory newSymbol) internal {
                        _symbol = newSymbol;
                      }
                      /**
                       * @notice Update the number of decimals for the token
                       * @param newDecimals The new number of decimals for the token
                       */
                      function _setDecimals(uint8 newDecimals) internal {
                        _decimals = newDecimals;
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IncentivizedERC20} from './IncentivizedERC20.sol';
                    /**
                     * @title MintableIncentivizedERC20
                     * @author Aave
                     * @notice Implements mint and burn functions for IncentivizedERC20
                     */
                    abstract contract MintableIncentivizedERC20 is IncentivizedERC20 {
                      /**
                       * @dev Constructor.
                       * @param pool The reference to the main Pool contract
                       * @param name The name of the token
                       * @param symbol The symbol of the token
                       * @param decimals The number of decimals of the token
                       */
                      constructor(
                        IPool pool,
                        string memory name,
                        string memory symbol,
                        uint8 decimals
                      ) IncentivizedERC20(pool, name, symbol, decimals) {
                        // Intentionally left blank
                      }
                      /**
                       * @notice Mints tokens to an account and apply incentives if defined
                       * @param account The address receiving tokens
                       * @param amount The amount of tokens to mint
                       */
                      function _mint(address account, uint128 amount) internal virtual {
                        uint256 oldTotalSupply = _totalSupply;
                        _totalSupply = oldTotalSupply + amount;
                        uint128 oldAccountBalance = _userState[account].balance;
                        _userState[account].balance = oldAccountBalance + amount;
                        IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                        if (address(incentivesControllerLocal) != address(0)) {
                          incentivesControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);
                        }
                      }
                      /**
                       * @notice Burns tokens from an account and apply incentives if defined
                       * @param account The account whose tokens are burnt
                       * @param amount The amount of tokens to burn
                       */
                      function _burn(address account, uint128 amount) internal virtual {
                        uint256 oldTotalSupply = _totalSupply;
                        _totalSupply = oldTotalSupply - amount;
                        uint128 oldAccountBalance = _userState[account].balance;
                        _userState[account].balance = oldAccountBalance - amount;
                        IAaveIncentivesController incentivesControllerLocal = _incentivesController;
                        if (address(incentivesControllerLocal) != address(0)) {
                          incentivesControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity 0.8.10;
                    import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {Errors} from '../../libraries/helpers/Errors.sol';
                    import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
                    import {IPool} from '../../../interfaces/IPool.sol';
                    import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol';
                    import {MintableIncentivizedERC20} from './MintableIncentivizedERC20.sol';
                    /**
                     * @title ScaledBalanceTokenBase
                     * @author Aave
                     * @notice Basic ERC20 implementation of scaled balance token
                     */
                    abstract contract ScaledBalanceTokenBase is MintableIncentivizedERC20, IScaledBalanceToken {
                      using WadRayMath for uint256;
                      using SafeCast for uint256;
                      /**
                       * @dev Constructor.
                       * @param pool The reference to the main Pool contract
                       * @param name The name of the token
                       * @param symbol The symbol of the token
                       * @param decimals The number of decimals of the token
                       */
                      constructor(
                        IPool pool,
                        string memory name,
                        string memory symbol,
                        uint8 decimals
                      ) MintableIncentivizedERC20(pool, name, symbol, decimals) {
                        // Intentionally left blank
                      }
                      /// @inheritdoc IScaledBalanceToken
                      function scaledBalanceOf(address user) external view override returns (uint256) {
                        return super.balanceOf(user);
                      }
                      /// @inheritdoc IScaledBalanceToken
                      function getScaledUserBalanceAndSupply(address user)
                        external
                        view
                        override
                        returns (uint256, uint256)
                      {
                        return (super.balanceOf(user), super.totalSupply());
                      }
                      /// @inheritdoc IScaledBalanceToken
                      function scaledTotalSupply() public view virtual override returns (uint256) {
                        return super.totalSupply();
                      }
                      /// @inheritdoc IScaledBalanceToken
                      function getPreviousIndex(address user) external view virtual override returns (uint256) {
                        return _userState[user].additionalData;
                      }
                      /**
                       * @notice Implements the basic logic to mint a scaled balance token.
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the scaled tokens
                       * @param amount The amount of tokens getting minted
                       * @param index The next liquidity index of the reserve
                       * @return `true` if the the previous balance of the user was 0
                       */
                      function _mintScaled(
                        address caller,
                        address onBehalfOf,
                        uint256 amount,
                        uint256 index
                      ) internal returns (bool) {
                        uint256 amountScaled = amount.rayDiv(index);
                        require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);
                        uint256 scaledBalance = super.balanceOf(onBehalfOf);
                        uint256 balanceIncrease = scaledBalance.rayMul(index) -
                          scaledBalance.rayMul(_userState[onBehalfOf].additionalData);
                        _userState[onBehalfOf].additionalData = index.toUint128();
                        _mint(onBehalfOf, amountScaled.toUint128());
                        uint256 amountToMint = amount + balanceIncrease;
                        emit Transfer(address(0), onBehalfOf, amountToMint);
                        emit Mint(caller, onBehalfOf, amountToMint, balanceIncrease, index);
                        return (scaledBalance == 0);
                      }
                      /**
                       * @notice Implements the basic logic to burn a scaled balance token.
                       * @dev In some instances, a burn transaction will emit a mint event
                       * if the amount to burn is less than the interest that the user accrued
                       * @param user The user which debt is burnt
                       * @param target The address that will receive the underlying, if any
                       * @param amount The amount getting burned
                       * @param index The variable debt index of the reserve
                       */
                      function _burnScaled(
                        address user,
                        address target,
                        uint256 amount,
                        uint256 index
                      ) internal {
                        uint256 amountScaled = amount.rayDiv(index);
                        require(amountScaled != 0, Errors.INVALID_BURN_AMOUNT);
                        uint256 scaledBalance = super.balanceOf(user);
                        uint256 balanceIncrease = scaledBalance.rayMul(index) -
                          scaledBalance.rayMul(_userState[user].additionalData);
                        _userState[user].additionalData = index.toUint128();
                        _burn(user, amountScaled.toUint128());
                        if (balanceIncrease > amount) {
                          uint256 amountToMint = balanceIncrease - amount;
                          emit Transfer(address(0), user, amountToMint);
                          emit Mint(user, user, amountToMint, balanceIncrease, index);
                        } else {
                          uint256 amountToBurn = amount - balanceIncrease;
                          emit Transfer(user, address(0), amountToBurn);
                          emit Burn(user, target, amountToBurn, balanceIncrease, index);
                        }
                      }
                      /**
                       * @notice Implements the basic logic to transfer scaled balance tokens between two users
                       * @dev It emits a mint event with the interest accrued per user
                       * @param sender The source address
                       * @param recipient The destination address
                       * @param amount The amount getting transferred
                       * @param index The next liquidity index of the reserve
                       */
                      function _transfer(
                        address sender,
                        address recipient,
                        uint256 amount,
                        uint256 index
                      ) internal {
                        uint256 senderScaledBalance = super.balanceOf(sender);
                        uint256 senderBalanceIncrease = senderScaledBalance.rayMul(index) -
                          senderScaledBalance.rayMul(_userState[sender].additionalData);
                        uint256 recipientScaledBalance = super.balanceOf(recipient);
                        uint256 recipientBalanceIncrease = recipientScaledBalance.rayMul(index) -
                          recipientScaledBalance.rayMul(_userState[recipient].additionalData);
                        _userState[sender].additionalData = index.toUint128();
                        _userState[recipient].additionalData = index.toUint128();
                        super._transfer(sender, recipient, amount.rayDiv(index).toUint128());
                        if (senderBalanceIncrease > 0) {
                          emit Transfer(address(0), sender, senderBalanceIncrease);
                          emit Mint(_msgSender(), sender, senderBalanceIncrease, senderBalanceIncrease, index);
                        }
                        if (sender != recipient && recipientBalanceIncrease > 0) {
                          emit Transfer(address(0), recipient, recipientBalanceIncrease);
                          emit Mint(_msgSender(), recipient, recipientBalanceIncrease, recipientBalanceIncrease, index);
                        }
                        emit Transfer(sender, recipient, amount);
                      }
                    }
                    

                    File 9 of 12: DefaultReserveInterestRateStrategyV2
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
                    import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol';
                    import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol';
                    import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                    import {Errors} from '../protocol/libraries/helpers/Errors.sol';
                    import {IDefaultInterestRateStrategyV2} from '../interfaces/IDefaultInterestRateStrategyV2.sol';
                    import {IReserveInterestRateStrategy} from '../interfaces/IReserveInterestRateStrategy.sol';
                    import {IPoolAddressesProvider} from '../interfaces/IPoolAddressesProvider.sol';
                    /**
                     * @title DefaultReserveInterestRateStrategyV2 contract
                     * @author BGD Labs
                     * @notice Default interest rate strategy used by the Aave protocol
                     * @dev Strategies are pool-specific: each contract CAN'T be used across different Aave pools
                     *   due to the caching of the PoolAddressesProvider and the usage of underlying addresses as
                     *   index of the _interestRateData
                     */
                    contract DefaultReserveInterestRateStrategyV2 is IDefaultInterestRateStrategyV2 {
                      using WadRayMath for uint256;
                      using PercentageMath for uint256;
                      struct CalcInterestRatesLocalVars {
                        uint256 availableLiquidity;
                        uint256 currentVariableBorrowRate;
                        uint256 currentLiquidityRate;
                        uint256 borrowUsageRatio;
                        uint256 supplyUsageRatio;
                        uint256 availableLiquidityPlusDebt;
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      uint256 public constant MAX_BORROW_RATE = 1000_00;
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      uint256 public constant MIN_OPTIMAL_POINT = 1_00;
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      uint256 public constant MAX_OPTIMAL_POINT = 99_00;
                      /// @dev Map of reserves address and their interest rate data (reserveAddress => interestRateData)
                      mapping(address => InterestRateData) internal _interestRateData;
                      modifier onlyPoolConfigurator() {
                        require(
                          msg.sender == ADDRESSES_PROVIDER.getPoolConfigurator(),
                          Errors.CALLER_NOT_POOL_CONFIGURATOR
                        );
                        _;
                      }
                      /**
                       * @dev Constructor.
                       * @param provider The address of the PoolAddressesProvider of the associated Aave pool
                       */
                      constructor(address provider) {
                        require(provider != address(0), Errors.INVALID_ADDRESSES_PROVIDER);
                        ADDRESSES_PROVIDER = IPoolAddressesProvider(provider);
                      }
                      /// @inheritdoc IReserveInterestRateStrategy
                      function setInterestRateParams(
                        address reserve,
                        bytes calldata rateData
                      ) external onlyPoolConfigurator {
                        _setInterestRateParams(reserve, abi.decode(rateData, (InterestRateData)));
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      function setInterestRateParams(
                        address reserve,
                        InterestRateData calldata rateData
                      ) external onlyPoolConfigurator {
                        _setInterestRateParams(reserve, rateData);
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      function getInterestRateData(address reserve) external view returns (InterestRateDataRay memory) {
                        return _rayifyRateData(_interestRateData[reserve]);
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      function getInterestRateDataBps(address reserve) external view returns (InterestRateData memory) {
                        return _interestRateData[reserve];
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      function getOptimalUsageRatio(address reserve) external view returns (uint256) {
                        return _bpsToRay(uint256(_interestRateData[reserve].optimalUsageRatio));
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      function getVariableRateSlope1(address reserve) external view returns (uint256) {
                        return _bpsToRay(uint256(_interestRateData[reserve].variableRateSlope1));
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      function getVariableRateSlope2(address reserve) external view returns (uint256) {
                        return _bpsToRay(uint256(_interestRateData[reserve].variableRateSlope2));
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      function getBaseVariableBorrowRate(address reserve) external view override returns (uint256) {
                        return _bpsToRay(uint256(_interestRateData[reserve].baseVariableBorrowRate));
                      }
                      /// @inheritdoc IDefaultInterestRateStrategyV2
                      function getMaxVariableBorrowRate(address reserve) external view override returns (uint256) {
                        return
                          _bpsToRay(
                            uint256(
                              _interestRateData[reserve].baseVariableBorrowRate +
                                _interestRateData[reserve].variableRateSlope1 +
                                _interestRateData[reserve].variableRateSlope2
                            )
                          );
                      }
                      /// @inheritdoc IReserveInterestRateStrategy
                      function calculateInterestRates(
                        DataTypes.CalculateInterestRatesParams memory params
                      ) external view virtual override returns (uint256, uint256) {
                        InterestRateDataRay memory rateData = _rayifyRateData(_interestRateData[params.reserve]);
                        // @note This is a short circuit to allow mintable assets (ex. GHO), which by definition cannot be supplied
                        // and thus do not use virtual underlying balances.
                        if (!params.usingVirtualBalance) {
                          return (0, rateData.baseVariableBorrowRate);
                        }
                        CalcInterestRatesLocalVars memory vars;
                        vars.currentLiquidityRate = 0;
                        vars.currentVariableBorrowRate = rateData.baseVariableBorrowRate;
                        if (params.totalDebt != 0) {
                          vars.availableLiquidity =
                            params.virtualUnderlyingBalance +
                            params.liquidityAdded -
                            params.liquidityTaken;
                          vars.availableLiquidityPlusDebt = vars.availableLiquidity + params.totalDebt;
                          vars.borrowUsageRatio = params.totalDebt.rayDiv(vars.availableLiquidityPlusDebt);
                          vars.supplyUsageRatio = params.totalDebt.rayDiv(
                            vars.availableLiquidityPlusDebt + params.unbacked
                          );
                        } else {
                          return (0, vars.currentVariableBorrowRate);
                        }
                        if (vars.borrowUsageRatio > rateData.optimalUsageRatio) {
                          uint256 excessBorrowUsageRatio = (vars.borrowUsageRatio - rateData.optimalUsageRatio).rayDiv(
                            WadRayMath.RAY - rateData.optimalUsageRatio
                          );
                          vars.currentVariableBorrowRate +=
                            rateData.variableRateSlope1 +
                            rateData.variableRateSlope2.rayMul(excessBorrowUsageRatio);
                        } else {
                          vars.currentVariableBorrowRate += rateData
                            .variableRateSlope1
                            .rayMul(vars.borrowUsageRatio)
                            .rayDiv(rateData.optimalUsageRatio);
                        }
                        vars.currentLiquidityRate = vars
                          .currentVariableBorrowRate
                          .rayMul(vars.supplyUsageRatio)
                          .percentMul(PercentageMath.PERCENTAGE_FACTOR - params.reserveFactor);
                        return (vars.currentLiquidityRate, vars.currentVariableBorrowRate);
                      }
                      /**
                       * @dev Doing validations and data update for an asset
                       * @param reserve address of the underlying asset of the reserve
                       * @param rateData Encoded reserve interest rate data to apply
                       */
                      function _setInterestRateParams(address reserve, InterestRateData memory rateData) internal {
                        require(reserve != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
                        require(
                          rateData.optimalUsageRatio <= MAX_OPTIMAL_POINT &&
                            rateData.optimalUsageRatio >= MIN_OPTIMAL_POINT,
                          Errors.INVALID_OPTIMAL_USAGE_RATIO
                        );
                        require(
                          rateData.variableRateSlope1 <= rateData.variableRateSlope2,
                          Errors.SLOPE_2_MUST_BE_GTE_SLOPE_1
                        );
                        // The maximum rate should not be above certain threshold
                        require(
                          uint256(rateData.baseVariableBorrowRate) +
                            uint256(rateData.variableRateSlope1) +
                            uint256(rateData.variableRateSlope2) <=
                            MAX_BORROW_RATE,
                          Errors.INVALID_MAX_RATE
                        );
                        _interestRateData[reserve] = rateData;
                        emit RateDataUpdate(
                          reserve,
                          rateData.optimalUsageRatio,
                          rateData.baseVariableBorrowRate,
                          rateData.variableRateSlope1,
                          rateData.variableRateSlope2
                        );
                      }
                      /**
                       * @dev Transforms an InterestRateData struct to an InterestRateDataRay struct by multiplying all values
                       * by 1e23, turning them into ray values
                       *
                       * @param data The InterestRateData struct to transform
                       *
                       * @return The resulting InterestRateDataRay struct
                       */
                      function _rayifyRateData(
                        InterestRateData memory data
                      ) internal pure returns (InterestRateDataRay memory) {
                        return
                          InterestRateDataRay({
                            optimalUsageRatio: _bpsToRay(uint256(data.optimalUsageRatio)),
                            baseVariableBorrowRate: _bpsToRay(uint256(data.baseVariableBorrowRate)),
                            variableRateSlope1: _bpsToRay(uint256(data.variableRateSlope1)),
                            variableRateSlope2: _bpsToRay(uint256(data.variableRateSlope2))
                          });
                      }
                      // @dev helper function added here, as generally the protocol doesn't use bps
                      function _bpsToRay(uint256 n) internal pure returns (uint256) {
                        return n * 1e23;
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @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: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @title WadRayMath library
                     * @author Aave
                     * @notice Provides functions to perform calculations with Wad and Ray units
                     * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
                     * with 27 digits of precision)
                     * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
                     */
                    library WadRayMath {
                      // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
                      uint256 internal constant WAD = 1e18;
                      uint256 internal constant HALF_WAD = 0.5e18;
                      uint256 internal constant RAY = 1e27;
                      uint256 internal constant HALF_RAY = 0.5e27;
                      uint256 internal constant WAD_RAY_RATIO = 1e9;
                      /**
                       * @dev Multiplies two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a*b, in wad
                       */
                      function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_WAD), WAD)
                        }
                      }
                      /**
                       * @dev Divides two wad, rounding half up to the nearest wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @param b Wad
                       * @return c = a/b, in wad
                       */
                      function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, WAD), div(b, 2)), b)
                        }
                      }
                      /**
                       * @notice Multiplies two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raymul b
                       */
                      function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
                        assembly {
                          if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, b), HALF_RAY), RAY)
                        }
                      }
                      /**
                       * @notice Divides two ray, rounding half up to the nearest ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @param b Ray
                       * @return c = a raydiv b
                       */
                      function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
                        // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
                        assembly {
                          if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
                            revert(0, 0)
                          }
                          c := div(add(mul(a, RAY), div(b, 2)), b)
                        }
                      }
                      /**
                       * @dev Casts ray down to wad
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Ray
                       * @return b = a converted to wad, rounded half up to the nearest wad
                       */
                      function rayToWad(uint256 a) internal pure returns (uint256 b) {
                        assembly {
                          b := div(a, WAD_RAY_RATIO)
                          let remainder := mod(a, WAD_RAY_RATIO)
                          if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
                            b := add(b, 1)
                          }
                        }
                      }
                      /**
                       * @dev Converts wad up to ray
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param a Wad
                       * @return b = a converted in ray
                       */
                      function wadToRay(uint256 a) internal pure returns (uint256 b) {
                        // to avoid overflow, b/WAD_RAY_RATIO == a
                        assembly {
                          b := mul(a, WAD_RAY_RATIO)
                          if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
                            revert(0, 0)
                          }
                        }
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.0;
                    /**
                     * @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. If a value is >=.5, will be rounded up, otherwise rounded down.
                     */
                    library PercentageMath {
                      // Maximum percentage factor (100.00%)
                      uint256 internal constant PERCENTAGE_FACTOR = 1e4;
                      // Half percentage factor (50.00%)
                      uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4;
                      /**
                       * @notice Executes a percentage multiplication
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param value The value of which the percentage needs to be calculated
                       * @param percentage The percentage of the value to be calculated
                       * @return result value percentmul percentage
                       */
                      function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                        // to avoid overflow, value <= (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentage
                        assembly {
                          if iszero(
                            or(
                              iszero(percentage),
                              iszero(gt(value, div(sub(not(0), HALF_PERCENTAGE_FACTOR), percentage)))
                            )
                          ) {
                            revert(0, 0)
                          }
                          result := div(add(mul(value, percentage), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
                        }
                      }
                      /**
                       * @notice Executes a percentage division
                       * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
                       * @param value The value of which the percentage needs to be calculated
                       * @param percentage The percentage of the value to be calculated
                       * @return result value percentdiv percentage
                       */
                      function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256 result) {
                        // to avoid overflow, value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR
                        assembly {
                          if or(
                            iszero(percentage),
                            iszero(iszero(gt(value, div(sub(not(0), div(percentage, 2)), PERCENTAGE_FACTOR))))
                          ) {
                            revert(0, 0)
                          }
                          result := div(add(mul(value, PERCENTAGE_FACTOR), div(percentage, 2)), percentage)
                        }
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    library DataTypes {
                      /**
                       * This exists specifically to maintain the `getReserveData()` interface, since the new, internal
                       * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`.
                       */
                      struct ReserveDataLegacy {
                        //stores the reserve configuration
                        ReserveConfigurationMap configuration;
                        //the liquidity index. Expressed in ray
                        uint128 liquidityIndex;
                        //the current supply rate. Expressed in ray
                        uint128 currentLiquidityRate;
                        //variable borrow index. Expressed in ray
                        uint128 variableBorrowIndex;
                        //the current variable borrow rate. Expressed in ray
                        uint128 currentVariableBorrowRate;
                        // DEPRECATED on v3.2.0
                        uint128 currentStableBorrowRate;
                        //timestamp of last update
                        uint40 lastUpdateTimestamp;
                        //the id of the reserve. Represents the position in the list of the active reserves
                        uint16 id;
                        //aToken address
                        address aTokenAddress;
                        // DEPRECATED on v3.2.0
                        address stableDebtTokenAddress;
                        //variableDebtToken address
                        address variableDebtTokenAddress;
                        //address of the interest rate strategy
                        address interestRateStrategyAddress;
                        //the current treasury balance, scaled
                        uint128 accruedToTreasury;
                        //the outstanding unbacked aTokens minted through the bridging feature
                        uint128 unbacked;
                        //the outstanding debt borrowed against this asset in isolation mode
                        uint128 isolationModeTotalDebt;
                      }
                      struct ReserveData {
                        //stores the reserve configuration
                        ReserveConfigurationMap configuration;
                        //the liquidity index. Expressed in ray
                        uint128 liquidityIndex;
                        //the current supply rate. Expressed in ray
                        uint128 currentLiquidityRate;
                        //variable borrow index. Expressed in ray
                        uint128 variableBorrowIndex;
                        //the current variable borrow rate. Expressed in ray
                        uint128 currentVariableBorrowRate;
                        // DEPRECATED on v3.2.0
                        uint128 __deprecatedStableBorrowRate;
                        //timestamp of last update
                        uint40 lastUpdateTimestamp;
                        //the id of the reserve. Represents the position in the list of the active reserves
                        uint16 id;
                        //timestamp until when liquidations are not allowed on the reserve, if set to past liquidations will be allowed
                        uint40 liquidationGracePeriodUntil;
                        //aToken address
                        address aTokenAddress;
                        // DEPRECATED on v3.2.0
                        address __deprecatedStableDebtTokenAddress;
                        //variableDebtToken address
                        address variableDebtTokenAddress;
                        //address of the interest rate strategy
                        address interestRateStrategyAddress;
                        //the current treasury balance, scaled
                        uint128 accruedToTreasury;
                        //the outstanding unbacked aTokens minted through the bridging feature
                        uint128 unbacked;
                        //the outstanding debt borrowed against this asset in isolation mode
                        uint128 isolationModeTotalDebt;
                        //the amount of underlying accounted for by the protocol
                        uint128 virtualUnderlyingBalance;
                      }
                      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: DEPRECATED: stable rate borrowing enabled
                        //bit 60: asset is paused
                        //bit 61: borrowing in isolation mode is enabled
                        //bit 62: siloed borrowing enabled
                        //bit 63: flashloaning enabled
                        //bit 64-79: reserve factor
                        //bit 80-115: borrow cap in whole tokens, borrowCap == 0 => no cap
                        //bit 116-151: supply cap in whole tokens, supplyCap == 0 => no cap
                        //bit 152-167: liquidation protocol fee
                        //bit 168-175: DEPRECATED: eMode category
                        //bit 176-211: unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
                        //bit 212-251: debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
                        //bit 252: virtual accounting is enabled for the reserve
                        //bit 253-255 unused
                        uint256 data;
                      }
                      struct UserConfigurationMap {
                        /**
                         * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
                         * The first bit indicates if an asset is used as collateral by the user, the second whether an
                         * asset is borrowed by the user.
                         */
                        uint256 data;
                      }
                      // DEPRECATED: kept for backwards compatibility, might be removed in a future version
                      struct EModeCategoryLegacy {
                        // each eMode category has a custom ltv and liquidation threshold
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        // DEPRECATED
                        address priceSource;
                        string label;
                      }
                      struct CollateralConfig {
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                      }
                      struct EModeCategoryBaseConfiguration {
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        string label;
                      }
                      struct EModeCategory {
                        // each eMode category has a custom ltv and liquidation threshold
                        uint16 ltv;
                        uint16 liquidationThreshold;
                        uint16 liquidationBonus;
                        uint128 collateralBitmap;
                        string label;
                        uint128 borrowableBitmap;
                      }
                      enum InterestRateMode {
                        NONE,
                        __DEPRECATED,
                        VARIABLE
                      }
                      struct ReserveCache {
                        uint256 currScaledVariableDebt;
                        uint256 nextScaledVariableDebt;
                        uint256 currLiquidityIndex;
                        uint256 nextLiquidityIndex;
                        uint256 currVariableBorrowIndex;
                        uint256 nextVariableBorrowIndex;
                        uint256 currLiquidityRate;
                        uint256 currVariableBorrowRate;
                        uint256 reserveFactor;
                        ReserveConfigurationMap reserveConfiguration;
                        address aTokenAddress;
                        address variableDebtTokenAddress;
                        uint40 reserveLastUpdateTimestamp;
                      }
                      struct ExecuteLiquidationCallParams {
                        uint256 reservesCount;
                        uint256 debtToCover;
                        address collateralAsset;
                        address debtAsset;
                        address user;
                        bool receiveAToken;
                        address priceOracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteSupplyParams {
                        address asset;
                        uint256 amount;
                        address onBehalfOf;
                        uint16 referralCode;
                      }
                      struct ExecuteBorrowParams {
                        address asset;
                        address user;
                        address onBehalfOf;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint16 referralCode;
                        bool releaseUnderlying;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                      }
                      struct ExecuteRepayParams {
                        address asset;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        address onBehalfOf;
                        bool useATokens;
                      }
                      struct ExecuteWithdrawParams {
                        address asset;
                        uint256 amount;
                        address to;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ExecuteSetUserEModeParams {
                        uint256 reservesCount;
                        address oracle;
                        uint8 categoryId;
                      }
                      struct FinalizeTransferParams {
                        address asset;
                        address from;
                        address to;
                        uint256 amount;
                        uint256 balanceFromBefore;
                        uint256 balanceToBefore;
                        uint256 reservesCount;
                        address oracle;
                        uint8 fromEModeCategory;
                      }
                      struct FlashloanParams {
                        address receiverAddress;
                        address[] assets;
                        uint256[] amounts;
                        uint256[] interestRateModes;
                        address onBehalfOf;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                        uint256 reservesCount;
                        address addressesProvider;
                        address pool;
                        uint8 userEModeCategory;
                        bool isAuthorizedFlashBorrower;
                      }
                      struct FlashloanSimpleParams {
                        address receiverAddress;
                        address asset;
                        uint256 amount;
                        bytes params;
                        uint16 referralCode;
                        uint256 flashLoanPremiumToProtocol;
                        uint256 flashLoanPremiumTotal;
                      }
                      struct FlashLoanRepaymentParams {
                        uint256 amount;
                        uint256 totalPremium;
                        uint256 flashLoanPremiumToProtocol;
                        address asset;
                        address receiverAddress;
                        uint16 referralCode;
                      }
                      struct CalculateUserAccountDataParams {
                        UserConfigurationMap userConfig;
                        uint256 reservesCount;
                        address user;
                        address oracle;
                        uint8 userEModeCategory;
                      }
                      struct ValidateBorrowParams {
                        ReserveCache reserveCache;
                        UserConfigurationMap userConfig;
                        address asset;
                        address userAddress;
                        uint256 amount;
                        InterestRateMode interestRateMode;
                        uint256 reservesCount;
                        address oracle;
                        uint8 userEModeCategory;
                        address priceOracleSentinel;
                        bool isolationModeActive;
                        address isolationModeCollateralAddress;
                        uint256 isolationModeDebtCeiling;
                      }
                      struct ValidateLiquidationCallParams {
                        ReserveCache debtReserveCache;
                        uint256 totalDebt;
                        uint256 healthFactor;
                        address priceOracleSentinel;
                      }
                      struct CalculateInterestRatesParams {
                        uint256 unbacked;
                        uint256 liquidityAdded;
                        uint256 liquidityTaken;
                        uint256 totalDebt;
                        uint256 reserveFactor;
                        address reserve;
                        bool usingVirtualBalance;
                        uint256 virtualUnderlyingBalance;
                      }
                      struct InitReserveParams {
                        address asset;
                        address aTokenAddress;
                        address variableDebtAddress;
                        address interestRateStrategyAddress;
                        uint16 reservesCount;
                        uint16 maxNumberReserves;
                      }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title Errors library
                     * @author Aave
                     * @notice Defines the error messages emitted by the different contracts of the Aave protocol
                     */
                    library Errors {
                      string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'
                      string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'
                      string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'
                      string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'
                      string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'
                      string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'
                      string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'
                      string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'
                      string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'
                      string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'
                      string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'
                      string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'
                      string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'
                      string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'
                      string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'
                      string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'
                      string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'
                      string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'
                      string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'
                      string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'
                      string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'
                      string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'
                      string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'
                      string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'
                      string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'
                      string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'
                      string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'
                      string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'
                      string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'
                      string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'
                      string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'
                      string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'
                      string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'
                      string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'
                      string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'
                      string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'
                      string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'
                      string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'
                      string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'
                      string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'
                      string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'
                      string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'
                      string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'
                      string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'
                      string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'
                      string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'
                      string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
                      string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
                      string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
                      string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
                      string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
                      string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
                      string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'
                      string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'
                      string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'
                      string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'
                      string public constant USER_IN_ISOLATION_MODE_OR_LTV_ZERO = '62'; // 'User is in isolation mode or ltv is zero'
                      string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'
                      string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'
                      string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'
                      string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'
                      string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'
                      string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'
                      string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'
                      string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'
                      string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'
                      string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'
                      string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve
                      string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'
                      string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'
                      string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'
                      string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'
                      string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'
                      string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'
                      string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'
                      string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'
                      string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'
                      string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'
                      string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'
                      string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'
                      string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'
                      string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'
                      string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0
                      string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled
                      string public constant INVALID_MAX_RATE = '92'; // The expect maximum borrow rate is invalid
                      string public constant WITHDRAW_TO_ATOKEN = '93'; // Withdrawing to the aToken is not allowed
                      string public constant SUPPLY_TO_ATOKEN = '94'; // Supplying to the aToken is not allowed
                      string public constant SLOPE_2_MUST_BE_GTE_SLOPE_1 = '95'; // Variable interest rate slope 2 can not be lower than slope 1
                      string public constant CALLER_NOT_RISK_OR_POOL_OR_EMERGENCY_ADMIN = '96'; // 'The caller of the function is not a risk, pool or emergency admin'
                      string public constant LIQUIDATION_GRACE_SENTINEL_CHECK_FAILED = '97'; // 'Liquidation grace sentinel validation failed'
                      string public constant INVALID_GRACE_PERIOD = '98'; // Grace period above a valid range
                      string public constant INVALID_FREEZE_STATE = '99'; // Reserve is already in the passed freeze state
                      string public constant NOT_BORROWABLE_IN_EMODE = '100'; // Asset not borrowable in eMode
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {IReserveInterestRateStrategy} from './IReserveInterestRateStrategy.sol';
                    import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                    /**
                     * @title IDefaultInterestRateStrategyV2
                     * @author BGD Labs
                     * @notice Interface of the default interest rate strategy used by the Aave protocol
                     */
                    interface IDefaultInterestRateStrategyV2 is IReserveInterestRateStrategy {
                      /**
                       * @notice Holds the interest rate data for a given reserve
                       *
                       * @dev Since values are in bps, they are multiplied by 1e23 in order to become rays with 27 decimals. This
                       * in turn means that the maximum supported interest rate is 4294967295 (2**32-1) bps or 42949672.95%.
                       *
                       * @param optimalUsageRatio The optimal usage ratio, in bps
                       * @param baseVariableBorrowRate The base variable borrow rate, in bps
                       * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio, in bps
                       * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio, in bps
                       */
                      struct InterestRateData {
                        uint16 optimalUsageRatio;
                        uint32 baseVariableBorrowRate;
                        uint32 variableRateSlope1;
                        uint32 variableRateSlope2;
                      }
                      /**
                       * @notice The interest rate data, where all values are in ray (fixed-point 27 decimal numbers) for a given reserve,
                       * used in in-memory calculations.
                       *
                       * @param optimalUsageRatio The optimal usage ratio
                       * @param baseVariableBorrowRate The base variable borrow rate
                       * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio
                       * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio
                       */
                      struct InterestRateDataRay {
                        uint256 optimalUsageRatio;
                        uint256 baseVariableBorrowRate;
                        uint256 variableRateSlope1;
                        uint256 variableRateSlope2;
                      }
                      /**
                       * @notice emitted when new interest rate data is set in a reserve
                       *
                       * @param reserve address of the reserve that has new interest rate data set
                       * @param optimalUsageRatio The optimal usage ratio, in bps
                       * @param baseVariableBorrowRate The base variable borrow rate, in bps
                       * @param variableRateSlope1 The slope of the variable interest curve, before hitting the optimal ratio, in bps
                       * @param variableRateSlope2 The slope of the variable interest curve, after hitting the optimal ratio, in bps
                       */
                      event RateDataUpdate(
                        address indexed reserve,
                        uint256 optimalUsageRatio,
                        uint256 baseVariableBorrowRate,
                        uint256 variableRateSlope1,
                        uint256 variableRateSlope2
                      );
                      /**
                       * @notice Returns the address of the PoolAddressesProvider
                       * @return The address of the PoolAddressesProvider contract
                       */
                      function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                      /**
                       * @notice Returns the maximum value achievable for variable borrow rate, in bps
                       * @return The maximum rate
                       */
                      function MAX_BORROW_RATE() external view returns (uint256);
                      /**
                       * @notice Returns the minimum optimal point, in bps
                       * @return The optimal point
                       */
                      function MIN_OPTIMAL_POINT() external view returns (uint256);
                      /**
                       * @notice Returns the maximum optimal point, in bps
                       * @return The optimal point
                       */
                      function MAX_OPTIMAL_POINT() external view returns (uint256);
                      /**
                       * notice Returns the full InterestRateData object for the given reserve, in ray
                       *
                       * @param reserve The reserve to get the data of
                       *
                       * @return The InterestRateDataRay object for the given reserve
                       */
                      function getInterestRateData(address reserve) external view returns (InterestRateDataRay memory);
                      /**
                       * notice Returns the full InterestRateDataRay object for the given reserve, in bps
                       *
                       * @param reserve The reserve to get the data of
                       *
                       * @return The InterestRateData object for the given reserve
                       */
                      function getInterestRateDataBps(address reserve) external view returns (InterestRateData memory);
                      /**
                       * @notice Returns the optimal usage rate for the given reserve in ray
                       *
                       * @param reserve The reserve to get the optimal usage rate of
                       *
                       * @return The optimal usage rate is the level of borrow / collateral at which the borrow rate
                       */
                      function getOptimalUsageRatio(address reserve) external view returns (uint256);
                      /**
                       * @notice Returns the variable rate slope below optimal usage ratio in ray
                       * @dev It's the variable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO
                       *
                       * @param reserve The reserve to get the variable rate slope 1 of
                       *
                       * @return The variable rate slope
                       */
                      function getVariableRateSlope1(address reserve) external view returns (uint256);
                      /**
                       * @notice Returns the variable rate slope above optimal usage ratio in ray
                       * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO
                       *
                       * @param reserve The reserve to get the variable rate slope 2 of
                       *
                       * @return The variable rate slope
                       */
                      function getVariableRateSlope2(address reserve) external view returns (uint256);
                      /**
                       * @notice Returns the base variable borrow rate, in ray
                       *
                       * @param reserve The reserve to get the base variable borrow rate of
                       *
                       * @return The base variable borrow rate
                       */
                      function getBaseVariableBorrowRate(address reserve) external view returns (uint256);
                      /**
                       * @notice Returns the maximum variable borrow rate, in ray
                       *
                       * @param reserve The reserve to get the maximum variable borrow rate of
                       *
                       * @return The maximum variable borrow rate
                       */
                      function getMaxVariableBorrowRate(address reserve) external view returns (uint256);
                      /**
                       * @notice Sets interest rate data for an Aave rate strategy
                       * @param reserve The reserve to update
                       * @param rateData The reserve interest rate data to apply to the given reserve
                       *   Being specific to this custom implementation, with custom struct type,
                       *   overloading the function on the generic interface
                       */
                      function setInterestRateParams(address reserve, InterestRateData calldata rateData) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                    /**
                     * @title IReserveInterestRateStrategy
                     * @author BGD Labs
                     * @notice Basic interface for any rate strategy used by the Aave protocol
                     */
                    interface IReserveInterestRateStrategy {
                      /**
                       * @notice Sets interest rate data for an Aave rate strategy
                       * @param reserve The reserve to update
                       * @param rateData The abi encoded reserve interest rate data to apply to the given reserve
                       *   Abstracted this way as rate strategies can be custom
                       */
                      function setInterestRateParams(address reserve, bytes calldata rateData) external;
                      /**
                       * @notice Calculates the interest rates depending on the reserve's state and configurations
                       * @param params The parameters needed to calculate interest rates
                       * @return liquidityRate The liquidity rate expressed in ray
                       * @return variableBorrowRate The variable borrow rate expressed in ray
                       */
                      function calculateInterestRates(
                        DataTypes.CalculateInterestRatesParams memory params
                      ) external view returns (uint256, uint256);
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity ^0.8.0;
                    /**
                     * @title IPoolAddressesProvider
                     * @author Aave
                     * @notice Defines the basic interface for a Pool Addresses Provider.
                     */
                    interface IPoolAddressesProvider {
                      /**
                       * @dev Emitted when the market identifier is updated.
                       * @param oldMarketId The old id of the market
                       * @param newMarketId The new id of the market
                       */
                      event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
                      /**
                       * @dev Emitted when the pool is updated.
                       * @param oldAddress The old address of the Pool
                       * @param newAddress The new address of the Pool
                       */
                      event PoolUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool configurator is updated.
                       * @param oldAddress The old address of the PoolConfigurator
                       * @param newAddress The new address of the PoolConfigurator
                       */
                      event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle is updated.
                       * @param oldAddress The old address of the PriceOracle
                       * @param newAddress The new address of the PriceOracle
                       */
                      event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL manager is updated.
                       * @param oldAddress The old address of the ACLManager
                       * @param newAddress The new address of the ACLManager
                       */
                      event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the ACL admin is updated.
                       * @param oldAddress The old address of the ACLAdmin
                       * @param newAddress The new address of the ACLAdmin
                       */
                      event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the price oracle sentinel is updated.
                       * @param oldAddress The old address of the PriceOracleSentinel
                       * @param newAddress The new address of the PriceOracleSentinel
                       */
                      event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the pool data provider is updated.
                       * @param oldAddress The old address of the PoolDataProvider
                       * @param newAddress The new address of the PoolDataProvider
                       */
                      event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when a new proxy is created.
                       * @param id The identifier of the proxy
                       * @param proxyAddress The address of the created proxy contract
                       * @param implementationAddress The address of the implementation contract
                       */
                      event ProxyCreated(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address indexed implementationAddress
                      );
                      /**
                       * @dev Emitted when a new non-proxied contract address is registered.
                       * @param id The identifier of the contract
                       * @param oldAddress The address of the old contract
                       * @param newAddress The address of the new contract
                       */
                      event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
                      /**
                       * @dev Emitted when the implementation of the proxy registered with id is updated
                       * @param id The identifier of the contract
                       * @param proxyAddress The address of the proxy contract
                       * @param oldImplementationAddress The address of the old implementation contract
                       * @param newImplementationAddress The address of the new implementation contract
                       */
                      event AddressSetAsProxy(
                        bytes32 indexed id,
                        address indexed proxyAddress,
                        address oldImplementationAddress,
                        address indexed newImplementationAddress
                      );
                      /**
                       * @notice Returns the id of the Aave market to which this contract points to.
                       * @return The market id
                       */
                      function getMarketId() external view returns (string memory);
                      /**
                       * @notice Associates an id with a specific PoolAddressesProvider.
                       * @dev This can be used to create an onchain registry of PoolAddressesProviders to
                       * identify and validate multiple Aave markets.
                       * @param newMarketId The market id
                       */
                      function setMarketId(string calldata newMarketId) external;
                      /**
                       * @notice Returns an address by its identifier.
                       * @dev The returned address might be an EOA or a contract, potentially proxied
                       * @dev It returns ZERO if there is no registered address with the given id
                       * @param id The id
                       * @return The address of the registered for the specified id
                       */
                      function getAddress(bytes32 id) external view returns (address);
                      /**
                       * @notice 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 `newImplementationAddress`.
                       * @dev 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 newImplementationAddress The address of the new implementation
                       */
                      function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
                      /**
                       * @notice Sets an address for an id replacing the address saved in the addresses map.
                       * @dev 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;
                      /**
                       * @notice Returns the address of the Pool proxy.
                       * @return The Pool proxy address
                       */
                      function getPool() external view returns (address);
                      /**
                       * @notice Updates the implementation of the Pool, or creates a proxy
                       * setting the new `pool` implementation when the function is called for the first time.
                       * @param newPoolImpl The new Pool implementation
                       */
                      function setPoolImpl(address newPoolImpl) external;
                      /**
                       * @notice Returns the address of the PoolConfigurator proxy.
                       * @return The PoolConfigurator proxy address
                       */
                      function getPoolConfigurator() external view returns (address);
                      /**
                       * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
                       * setting the new `PoolConfigurator` implementation when the function is called for the first time.
                       * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
                       */
                      function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
                      /**
                       * @notice Returns the address of the price oracle.
                       * @return The address of the PriceOracle
                       */
                      function getPriceOracle() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle.
                       * @param newPriceOracle The address of the new PriceOracle
                       */
                      function setPriceOracle(address newPriceOracle) external;
                      /**
                       * @notice Returns the address of the ACL manager.
                       * @return The address of the ACLManager
                       */
                      function getACLManager() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL manager.
                       * @param newAclManager The address of the new ACLManager
                       */
                      function setACLManager(address newAclManager) external;
                      /**
                       * @notice Returns the address of the ACL admin.
                       * @return The address of the ACL admin
                       */
                      function getACLAdmin() external view returns (address);
                      /**
                       * @notice Updates the address of the ACL admin.
                       * @param newAclAdmin The address of the new ACL admin
                       */
                      function setACLAdmin(address newAclAdmin) external;
                      /**
                       * @notice Returns the address of the price oracle sentinel.
                       * @return The address of the PriceOracleSentinel
                       */
                      function getPriceOracleSentinel() external view returns (address);
                      /**
                       * @notice Updates the address of the price oracle sentinel.
                       * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
                       */
                      function setPriceOracleSentinel(address newPriceOracleSentinel) external;
                      /**
                       * @notice Returns the address of the data provider.
                       * @return The address of the DataProvider
                       */
                      function getPoolDataProvider() external view returns (address);
                      /**
                       * @notice Updates the address of the data provider.
                       * @param newDataProvider The address of the new DataProvider
                       */
                      function setPoolDataProvider(address newDataProvider) external;
                    }
                    

                    File 10 of 12: FiatTokenV2_2
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { EIP712Domain } from "./EIP712Domain.sol"; // solhint-disable-line no-unused-import
                    import { Blacklistable } from "../v1/Blacklistable.sol"; // solhint-disable-line no-unused-import
                    import { FiatTokenV1 } from "../v1/FiatTokenV1.sol"; // solhint-disable-line no-unused-import
                    import { FiatTokenV2 } from "./FiatTokenV2.sol"; // solhint-disable-line no-unused-import
                    import { FiatTokenV2_1 } from "./FiatTokenV2_1.sol";
                    import { EIP712 } from "../util/EIP712.sol";
                    // solhint-disable func-name-mixedcase
                    /**
                     * @title FiatToken V2.2
                     * @notice ERC20 Token backed by fiat reserves, version 2.2
                     */
                    contract FiatTokenV2_2 is FiatTokenV2_1 {
                        /**
                         * @notice Initialize v2.2
                         * @param accountsToBlacklist   A list of accounts to migrate from the old blacklist
                         * @param newSymbol             New token symbol
                         * data structure to the new blacklist data structure.
                         */
                        function initializeV2_2(
                            address[] calldata accountsToBlacklist,
                            string calldata newSymbol
                        ) external {
                            // solhint-disable-next-line reason-string
                            require(_initializedVersion == 2);
                            // Update fiat token symbol
                            symbol = newSymbol;
                            // Add previously blacklisted accounts to the new blacklist data structure
                            // and remove them from the old blacklist data structure.
                            for (uint256 i = 0; i < accountsToBlacklist.length; i++) {
                                require(
                                    _deprecatedBlacklisted[accountsToBlacklist[i]],
                                    "FiatTokenV2_2: Blacklisting previously unblacklisted account!"
                                );
                                _blacklist(accountsToBlacklist[i]);
                                delete _deprecatedBlacklisted[accountsToBlacklist[i]];
                            }
                            _blacklist(address(this));
                            delete _deprecatedBlacklisted[address(this)];
                            _initializedVersion = 3;
                        }
                        /**
                         * @dev Internal function to get the current chain id.
                         * @return The current chain id.
                         */
                        function _chainId() internal virtual view returns (uint256) {
                            uint256 chainId;
                            assembly {
                                chainId := chainid()
                            }
                            return chainId;
                        }
                        /**
                         * @inheritdoc EIP712Domain
                         */
                        function _domainSeparator() internal override view returns (bytes32) {
                            return EIP712.makeDomainSeparator(name, "2", _chainId());
                        }
                        /**
                         * @notice Update allowance with a signed permit
                         * @dev EOA wallet signatures should be packed in the order of r, s, v.
                         * @param owner       Token owner's address (Authorizer)
                         * @param spender     Spender's address
                         * @param value       Amount of allowance
                         * @param deadline    The time at which the signature expires (unix time), or max uint256 value to signal no expiration
                         * @param signature   Signature bytes signed by an EOA wallet or a contract wallet
                         */
                        function permit(
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            bytes memory signature
                        ) external whenNotPaused {
                            _permit(owner, spender, value, deadline, signature);
                        }
                        /**
                         * @notice Execute a transfer with a signed authorization
                         * @dev EOA wallet signatures should be packed in the order of r, s, v.
                         * @param from          Payer's address (Authorizer)
                         * @param to            Payee's address
                         * @param value         Amount to be transferred
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         * @param nonce         Unique nonce
                         * @param signature     Signature bytes signed by an EOA wallet or a contract wallet
                         */
                        function transferWithAuthorization(
                            address from,
                            address to,
                            uint256 value,
                            uint256 validAfter,
                            uint256 validBefore,
                            bytes32 nonce,
                            bytes memory signature
                        ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                            _transferWithAuthorization(
                                from,
                                to,
                                value,
                                validAfter,
                                validBefore,
                                nonce,
                                signature
                            );
                        }
                        /**
                         * @notice Receive a transfer with a signed authorization from the payer
                         * @dev This has an additional check to ensure that the payee's address
                         * matches the caller of this function to prevent front-running attacks.
                         * EOA wallet signatures should be packed in the order of r, s, v.
                         * @param from          Payer's address (Authorizer)
                         * @param to            Payee's address
                         * @param value         Amount to be transferred
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         * @param nonce         Unique nonce
                         * @param signature     Signature bytes signed by an EOA wallet or a contract wallet
                         */
                        function receiveWithAuthorization(
                            address from,
                            address to,
                            uint256 value,
                            uint256 validAfter,
                            uint256 validBefore,
                            bytes32 nonce,
                            bytes memory signature
                        ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                            _receiveWithAuthorization(
                                from,
                                to,
                                value,
                                validAfter,
                                validBefore,
                                nonce,
                                signature
                            );
                        }
                        /**
                         * @notice Attempt to cancel an authorization
                         * @dev Works only if the authorization is not yet used.
                         * EOA wallet signatures should be packed in the order of r, s, v.
                         * @param authorizer    Authorizer's address
                         * @param nonce         Nonce of the authorization
                         * @param signature     Signature bytes signed by an EOA wallet or a contract wallet
                         */
                        function cancelAuthorization(
                            address authorizer,
                            bytes32 nonce,
                            bytes memory signature
                        ) external whenNotPaused {
                            _cancelAuthorization(authorizer, nonce, signature);
                        }
                        /**
                         * @dev Helper method that sets the blacklist state of an account on balanceAndBlacklistStates.
                         * If _shouldBlacklist is true, we apply a (1 << 255) bitmask with an OR operation on the
                         * account's balanceAndBlacklistState. This flips the high bit for the account to 1,
                         * indicating that the account is blacklisted.
                         *
                         * If _shouldBlacklist if false, we reset the account's balanceAndBlacklistStates to their
                         * balances. This clears the high bit for the account, indicating that the account is unblacklisted.
                         * @param _account         The address of the account.
                         * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted.
                         */
                        function _setBlacklistState(address _account, bool _shouldBlacklist)
                            internal
                            override
                        {
                            balanceAndBlacklistStates[_account] = _shouldBlacklist
                                ? balanceAndBlacklistStates[_account] | (1 << 255)
                                : _balanceOf(_account);
                        }
                        /**
                         * @dev Helper method that sets the balance of an account on balanceAndBlacklistStates.
                         * Since balances are stored in the last 255 bits of the balanceAndBlacklistStates value,
                         * we need to ensure that the updated balance does not exceed (2^255 - 1).
                         * Since blacklisted accounts' balances cannot be updated, the method will also
                         * revert if the account is blacklisted
                         * @param _account The address of the account.
                         * @param _balance The new fiat token balance of the account (max: (2^255 - 1)).
                         */
                        function _setBalance(address _account, uint256 _balance) internal override {
                            require(
                                _balance <= ((1 << 255) - 1),
                                "FiatTokenV2_2: Balance exceeds (2^255 - 1)"
                            );
                            require(
                                !_isBlacklisted(_account),
                                "FiatTokenV2_2: Account is blacklisted"
                            );
                            balanceAndBlacklistStates[_account] = _balance;
                        }
                        /**
                         * @inheritdoc Blacklistable
                         */
                        function _isBlacklisted(address _account)
                            internal
                            override
                            view
                            returns (bool)
                        {
                            return balanceAndBlacklistStates[_account] >> 255 == 1;
                        }
                        /**
                         * @dev Helper method to obtain the balance of an account. Since balances
                         * are stored in the last 255 bits of the balanceAndBlacklistStates value,
                         * we apply a ((1 << 255) - 1) bit bitmask with an AND operation on the
                         * balanceAndBlacklistState to obtain the balance.
                         * @param _account  The address of the account.
                         * @return          The fiat token balance of the account.
                         */
                        function _balanceOf(address _account)
                            internal
                            override
                            view
                            returns (uint256)
                        {
                            return balanceAndBlacklistStates[_account] & ((1 << 255) - 1);
                        }
                        /**
                         * @inheritdoc FiatTokenV1
                         */
                        function approve(address spender, uint256 value)
                            external
                            override
                            whenNotPaused
                            returns (bool)
                        {
                            _approve(msg.sender, spender, value);
                            return true;
                        }
                        /**
                         * @inheritdoc FiatTokenV2
                         */
                        function permit(
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) external override whenNotPaused {
                            _permit(owner, spender, value, deadline, v, r, s);
                        }
                        /**
                         * @inheritdoc FiatTokenV2
                         */
                        function increaseAllowance(address spender, uint256 increment)
                            external
                            override
                            whenNotPaused
                            returns (bool)
                        {
                            _increaseAllowance(msg.sender, spender, increment);
                            return true;
                        }
                        /**
                         * @inheritdoc FiatTokenV2
                         */
                        function decreaseAllowance(address spender, uint256 decrement)
                            external
                            override
                            whenNotPaused
                            returns (bool)
                        {
                            _decreaseAllowance(msg.sender, spender, decrement);
                            return true;
                        }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.2 <0.8.0;
                    /**
                     * @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) {
                            // This method relies on extcodesize, which returns 0 for contracts in
                            // construction, since the code is only stored at the end of the
                            // constructor execution.
                            uint256 size;
                            // solhint-disable-next-line no-inline-assembly
                            assembly { size := extcodesize(account) }
                            return size > 0;
                        }
                        /**
                         * @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");
                        }
                        /**
                         * @dev Performs a Solidity function call using a low level `call`. A
                         * plain`call` is an unsafe replacement for a function call: use this
                         * function instead.
                         *
                         * If `target` reverts with a revert reason, it is bubbled up by this
                         * function (like regular Solidity function calls).
                         *
                         * Returns the raw returned data. To convert to the expected return value,
                         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                         *
                         * Requirements:
                         *
                         * - `target` must be a contract.
                         * - calling `target` with `data` must not revert.
                         *
                         * _Available since v3.1._
                         */
                        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return functionCall(target, data, "Address: low-level call failed");
                        }
                        /**
                         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                         * `errorMessage` as a fallback revert reason when `target` reverts.
                         *
                         * _Available since v3.1._
                         */
                        function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                            return functionCallWithValue(target, data, 0, errorMessage);
                        }
                        /**
                         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                         * but also transferring `value` wei to `target`.
                         *
                         * Requirements:
                         *
                         * - the calling contract must have an ETH balance of at least `value`.
                         * - the called Solidity function must be `payable`.
                         *
                         * _Available since v3.1._
                         */
                        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");
                        }
                        /**
                         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                         * with `errorMessage` as a fallback revert reason when `target` reverts.
                         *
                         * _Available since v3.1._
                         */
                        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");
                            require(isContract(target), "Address: call to non-contract");
                            // solhint-disable-next-line avoid-low-level-calls
                            (bool success, bytes memory returndata) = target.call{ value: value }(data);
                            return _verifyCallResult(success, returndata, errorMessage);
                        }
                        /**
                         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                         * but performing a static call.
                         *
                         * _Available since v3.3._
                         */
                        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                            return functionStaticCall(target, data, "Address: low-level static call failed");
                        }
                        /**
                         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                         * but performing a static call.
                         *
                         * _Available since v3.3._
                         */
                        function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                            require(isContract(target), "Address: static call to non-contract");
                            // solhint-disable-next-line avoid-low-level-calls
                            (bool success, bytes memory returndata) = target.staticcall(data);
                            return _verifyCallResult(success, returndata, errorMessage);
                        }
                        /**
                         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                         * but performing a delegate call.
                         *
                         * _Available since v3.4._
                         */
                        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                        }
                        /**
                         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                         * but performing a delegate call.
                         *
                         * _Available since v3.4._
                         */
                        function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                            require(isContract(target), "Address: delegate call to non-contract");
                            // solhint-disable-next-line avoid-low-level-calls
                            (bool success, bytes memory returndata) = target.delegatecall(data);
                            return _verifyCallResult(success, returndata, errorMessage);
                        }
                        function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                            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);
                                }
                            }
                        }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.0 <0.8.0;
                    import "./IERC20.sol";
                    import "../../math/SafeMath.sol";
                    import "../../utils/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));
                        }
                        /**
                         * @dev Deprecated. This function has issues similar to the ones found in
                         * {IERC20-approve}, and its usage is discouraged.
                         *
                         * Whenever possible, use {safeIncreaseAllowance} and
                         * {safeDecreaseAllowance} instead.
                         */
                        function safeApprove(IERC20 token, address spender, uint256 value) internal {
                            // safeApprove should only be called when setting an initial allowance,
                            // or when resetting it to zero. To increase and decrease it, use
                            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                            // solhint-disable-next-line max-line-length
                            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 safeIncreaseAllowance(IERC20 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(IERC20 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));
                        }
                        /**
                         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                         * on the return value: the return value is optional (but if data is returned, it must not be false).
                         * @param token The token targeted by the call.
                         * @param data The call data (encoded using abi.encode or one of its variants).
                         */
                        function _callOptionalReturn(IERC20 token, bytes memory data) private {
                            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                            // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                            // the target address contains contract code and also asserts for success in the low-level call.
                            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");
                            }
                        }
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity >=0.6.0 <0.8.0;
                    /**
                     * @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.0 <0.8.0;
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            uint256 c = a + b;
                            if (c < a) return (false, 0);
                            return (true, c);
                        }
                        /**
                         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b > a) return (false, 0);
                            return (true, a - b);
                        }
                        /**
                         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                            if (a == 0) return (true, 0);
                            uint256 c = a * b;
                            if (c / a != b) return (false, 0);
                            return (true, c);
                        }
                        /**
                         * @dev Returns the division of two unsigned integers, with a division by zero flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b == 0) return (false, 0);
                            return (true, a / b);
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                         *
                         * _Available since v3.4._
                         */
                        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                            if (b == 0) return (false, 0);
                            return (true, a % b);
                        }
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         *
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                            return c;
                        }
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         *
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a, "SafeMath: subtraction overflow");
                            return a - b;
                        }
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         *
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            if (a == 0) return 0;
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                            return c;
                        }
                        /**
                         * @dev Returns the integer division of two unsigned integers, reverting on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0, "SafeMath: division by zero");
                            return a / b;
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * reverting when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0, "SafeMath: modulo by zero");
                            return a % b;
                        }
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                         * overflow (when the result is negative).
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {trySub}.
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         *
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b <= a, errorMessage);
                            return a - b;
                        }
                        /**
                         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                         * division by zero. The result is rounded towards zero.
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {tryDiv}.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b > 0, errorMessage);
                            return a / b;
                        }
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * reverting with custom message when dividing by zero.
                         *
                         * CAUTION: This function is deprecated because it requires allocating memory for the error
                         * message unnecessarily. For custom revert reasons use {tryMod}.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         *
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b > 0, errorMessage);
                            return a % b;
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { FiatTokenV2 } from "./FiatTokenV2.sol";
                    // solhint-disable func-name-mixedcase
                    /**
                     * @title FiatToken V2.1
                     * @notice ERC20 Token backed by fiat reserves, version 2.1
                     */
                    contract FiatTokenV2_1 is FiatTokenV2 {
                        /**
                         * @notice Initialize v2.1
                         * @param lostAndFound  The address to which the locked funds are sent
                         */
                        function initializeV2_1(address lostAndFound) external {
                            // solhint-disable-next-line reason-string
                            require(_initializedVersion == 1);
                            uint256 lockedAmount = _balanceOf(address(this));
                            if (lockedAmount > 0) {
                                _transfer(address(this), lostAndFound, lockedAmount);
                            }
                            _blacklist(address(this));
                            _initializedVersion = 2;
                        }
                        /**
                         * @notice Version string for the EIP712 domain separator
                         * @return Version string
                         */
                        function version() external pure returns (string memory) {
                            return "2";
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { FiatTokenV1_1 } from "../v1.1/FiatTokenV1_1.sol";
                    import { EIP712 } from "../util/EIP712.sol";
                    import { EIP3009 } from "./EIP3009.sol";
                    import { EIP2612 } from "./EIP2612.sol";
                    /**
                     * @title FiatToken V2
                     * @notice ERC20 Token backed by fiat reserves, version 2
                     */
                    contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 {
                        uint8 internal _initializedVersion;
                        /**
                         * @notice Initialize v2
                         * @param newName   New token name
                         */
                        function initializeV2(string calldata newName) external {
                            // solhint-disable-next-line reason-string
                            require(initialized && _initializedVersion == 0);
                            name = newName;
                            _DEPRECATED_CACHED_DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(
                                newName,
                                "2"
                            );
                            _initializedVersion = 1;
                        }
                        /**
                         * @notice Increase the allowance by a given increment
                         * @param spender   Spender's address
                         * @param increment Amount of increase in allowance
                         * @return True if successful
                         */
                        function increaseAllowance(address spender, uint256 increment)
                            external
                            virtual
                            whenNotPaused
                            notBlacklisted(msg.sender)
                            notBlacklisted(spender)
                            returns (bool)
                        {
                            _increaseAllowance(msg.sender, spender, increment);
                            return true;
                        }
                        /**
                         * @notice Decrease the allowance by a given decrement
                         * @param spender   Spender's address
                         * @param decrement Amount of decrease in allowance
                         * @return True if successful
                         */
                        function decreaseAllowance(address spender, uint256 decrement)
                            external
                            virtual
                            whenNotPaused
                            notBlacklisted(msg.sender)
                            notBlacklisted(spender)
                            returns (bool)
                        {
                            _decreaseAllowance(msg.sender, spender, decrement);
                            return true;
                        }
                        /**
                         * @notice Execute a transfer with a signed authorization
                         * @param from          Payer's address (Authorizer)
                         * @param to            Payee's address
                         * @param value         Amount to be transferred
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         * @param nonce         Unique nonce
                         * @param v             v of the signature
                         * @param r             r of the signature
                         * @param s             s of the signature
                         */
                        function transferWithAuthorization(
                            address from,
                            address to,
                            uint256 value,
                            uint256 validAfter,
                            uint256 validBefore,
                            bytes32 nonce,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                            _transferWithAuthorization(
                                from,
                                to,
                                value,
                                validAfter,
                                validBefore,
                                nonce,
                                v,
                                r,
                                s
                            );
                        }
                        /**
                         * @notice Receive a transfer with a signed authorization from the payer
                         * @dev This has an additional check to ensure that the payee's address
                         * matches the caller of this function to prevent front-running attacks.
                         * @param from          Payer's address (Authorizer)
                         * @param to            Payee's address
                         * @param value         Amount to be transferred
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         * @param nonce         Unique nonce
                         * @param v             v of the signature
                         * @param r             r of the signature
                         * @param s             s of the signature
                         */
                        function receiveWithAuthorization(
                            address from,
                            address to,
                            uint256 value,
                            uint256 validAfter,
                            uint256 validBefore,
                            bytes32 nonce,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                            _receiveWithAuthorization(
                                from,
                                to,
                                value,
                                validAfter,
                                validBefore,
                                nonce,
                                v,
                                r,
                                s
                            );
                        }
                        /**
                         * @notice Attempt to cancel an authorization
                         * @dev Works only if the authorization is not yet used.
                         * @param authorizer    Authorizer's address
                         * @param nonce         Nonce of the authorization
                         * @param v             v of the signature
                         * @param r             r of the signature
                         * @param s             s of the signature
                         */
                        function cancelAuthorization(
                            address authorizer,
                            bytes32 nonce,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) external whenNotPaused {
                            _cancelAuthorization(authorizer, nonce, v, r, s);
                        }
                        /**
                         * @notice Update allowance with a signed permit
                         * @param owner       Token owner's address (Authorizer)
                         * @param spender     Spender's address
                         * @param value       Amount of allowance
                         * @param deadline    The time at which the signature expires (unix time), or max uint256 value to signal no expiration
                         * @param v           v of the signature
                         * @param r           r of the signature
                         * @param s           s of the signature
                         */
                        function permit(
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        )
                            external
                            virtual
                            whenNotPaused
                            notBlacklisted(owner)
                            notBlacklisted(spender)
                        {
                            _permit(owner, spender, value, deadline, v, r, s);
                        }
                        /**
                         * @dev Internal function to increase the allowance by a given increment
                         * @param owner     Token owner's address
                         * @param spender   Spender's address
                         * @param increment Amount of increase
                         */
                        function _increaseAllowance(
                            address owner,
                            address spender,
                            uint256 increment
                        ) internal override {
                            _approve(owner, spender, allowed[owner][spender].add(increment));
                        }
                        /**
                         * @dev Internal function to decrease the allowance by a given decrement
                         * @param owner     Token owner's address
                         * @param spender   Spender's address
                         * @param decrement Amount of decrease
                         */
                        function _decreaseAllowance(
                            address owner,
                            address spender,
                            uint256 decrement
                        ) internal override {
                            _approve(
                                owner,
                                spender,
                                allowed[owner][spender].sub(
                                    decrement,
                                    "ERC20: decreased allowance below zero"
                                )
                            );
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    // solhint-disable func-name-mixedcase
                    /**
                     * @title EIP712 Domain
                     */
                    contract EIP712Domain {
                        // was originally DOMAIN_SEPARATOR
                        // but that has been moved to a method so we can override it in V2_2+
                        bytes32 internal _DEPRECATED_CACHED_DOMAIN_SEPARATOR;
                        /**
                         * @notice Get the EIP712 Domain Separator.
                         * @return The bytes32 EIP712 domain separator.
                         */
                        function DOMAIN_SEPARATOR() external view returns (bytes32) {
                            return _domainSeparator();
                        }
                        /**
                         * @dev Internal method to get the EIP712 Domain Separator.
                         * @return The bytes32 EIP712 domain separator.
                         */
                        function _domainSeparator() internal virtual view returns (bytes32) {
                            return _DEPRECATED_CACHED_DOMAIN_SEPARATOR;
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { AbstractFiatTokenV2 } from "./AbstractFiatTokenV2.sol";
                    import { EIP712Domain } from "./EIP712Domain.sol";
                    import { SignatureChecker } from "../util/SignatureChecker.sol";
                    import { MessageHashUtils } from "../util/MessageHashUtils.sol";
                    /**
                     * @title EIP-3009
                     * @notice Provide internal implementation for gas-abstracted transfers
                     * @dev Contracts that inherit from this must wrap these with publicly
                     * accessible functions, optionally adding modifiers where necessary
                     */
                    abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain {
                        // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
                        bytes32
                            public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
                        // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
                        bytes32
                            public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
                        // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
                        bytes32
                            public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
                        /**
                         * @dev authorizer address => nonce => bool (true if nonce is used)
                         */
                        mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
                        event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
                        event AuthorizationCanceled(
                            address indexed authorizer,
                            bytes32 indexed nonce
                        );
                        /**
                         * @notice Returns the state of an authorization
                         * @dev Nonces are randomly generated 32-byte data unique to the
                         * authorizer's address
                         * @param authorizer    Authorizer's address
                         * @param nonce         Nonce of the authorization
                         * @return True if the nonce is used
                         */
                        function authorizationState(address authorizer, bytes32 nonce)
                            external
                            view
                            returns (bool)
                        {
                            return _authorizationStates[authorizer][nonce];
                        }
                        /**
                         * @notice Execute a transfer with a signed authorization
                         * @param from          Payer's address (Authorizer)
                         * @param to            Payee's address
                         * @param value         Amount to be transferred
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         * @param nonce         Unique nonce
                         * @param v             v of the signature
                         * @param r             r of the signature
                         * @param s             s of the signature
                         */
                        function _transferWithAuthorization(
                            address from,
                            address to,
                            uint256 value,
                            uint256 validAfter,
                            uint256 validBefore,
                            bytes32 nonce,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) internal {
                            _transferWithAuthorization(
                                from,
                                to,
                                value,
                                validAfter,
                                validBefore,
                                nonce,
                                abi.encodePacked(r, s, v)
                            );
                        }
                        /**
                         * @notice Execute a transfer with a signed authorization
                         * @dev EOA wallet signatures should be packed in the order of r, s, v.
                         * @param from          Payer's address (Authorizer)
                         * @param to            Payee's address
                         * @param value         Amount to be transferred
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         * @param nonce         Unique nonce
                         * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
                         */
                        function _transferWithAuthorization(
                            address from,
                            address to,
                            uint256 value,
                            uint256 validAfter,
                            uint256 validBefore,
                            bytes32 nonce,
                            bytes memory signature
                        ) internal {
                            _requireValidAuthorization(from, nonce, validAfter, validBefore);
                            _requireValidSignature(
                                from,
                                keccak256(
                                    abi.encode(
                                        TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                                        from,
                                        to,
                                        value,
                                        validAfter,
                                        validBefore,
                                        nonce
                                    )
                                ),
                                signature
                            );
                            _markAuthorizationAsUsed(from, nonce);
                            _transfer(from, to, value);
                        }
                        /**
                         * @notice Receive a transfer with a signed authorization from the payer
                         * @dev This has an additional check to ensure that the payee's address
                         * matches the caller of this function to prevent front-running attacks.
                         * @param from          Payer's address (Authorizer)
                         * @param to            Payee's address
                         * @param value         Amount to be transferred
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         * @param nonce         Unique nonce
                         * @param v             v of the signature
                         * @param r             r of the signature
                         * @param s             s of the signature
                         */
                        function _receiveWithAuthorization(
                            address from,
                            address to,
                            uint256 value,
                            uint256 validAfter,
                            uint256 validBefore,
                            bytes32 nonce,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) internal {
                            _receiveWithAuthorization(
                                from,
                                to,
                                value,
                                validAfter,
                                validBefore,
                                nonce,
                                abi.encodePacked(r, s, v)
                            );
                        }
                        /**
                         * @notice Receive a transfer with a signed authorization from the payer
                         * @dev This has an additional check to ensure that the payee's address
                         * matches the caller of this function to prevent front-running attacks.
                         * EOA wallet signatures should be packed in the order of r, s, v.
                         * @param from          Payer's address (Authorizer)
                         * @param to            Payee's address
                         * @param value         Amount to be transferred
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         * @param nonce         Unique nonce
                         * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
                         */
                        function _receiveWithAuthorization(
                            address from,
                            address to,
                            uint256 value,
                            uint256 validAfter,
                            uint256 validBefore,
                            bytes32 nonce,
                            bytes memory signature
                        ) internal {
                            require(to == msg.sender, "FiatTokenV2: caller must be the payee");
                            _requireValidAuthorization(from, nonce, validAfter, validBefore);
                            _requireValidSignature(
                                from,
                                keccak256(
                                    abi.encode(
                                        RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
                                        from,
                                        to,
                                        value,
                                        validAfter,
                                        validBefore,
                                        nonce
                                    )
                                ),
                                signature
                            );
                            _markAuthorizationAsUsed(from, nonce);
                            _transfer(from, to, value);
                        }
                        /**
                         * @notice Attempt to cancel an authorization
                         * @param authorizer    Authorizer's address
                         * @param nonce         Nonce of the authorization
                         * @param v             v of the signature
                         * @param r             r of the signature
                         * @param s             s of the signature
                         */
                        function _cancelAuthorization(
                            address authorizer,
                            bytes32 nonce,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) internal {
                            _cancelAuthorization(authorizer, nonce, abi.encodePacked(r, s, v));
                        }
                        /**
                         * @notice Attempt to cancel an authorization
                         * @dev EOA wallet signatures should be packed in the order of r, s, v.
                         * @param authorizer    Authorizer's address
                         * @param nonce         Nonce of the authorization
                         * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
                         */
                        function _cancelAuthorization(
                            address authorizer,
                            bytes32 nonce,
                            bytes memory signature
                        ) internal {
                            _requireUnusedAuthorization(authorizer, nonce);
                            _requireValidSignature(
                                authorizer,
                                keccak256(
                                    abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce)
                                ),
                                signature
                            );
                            _authorizationStates[authorizer][nonce] = true;
                            emit AuthorizationCanceled(authorizer, nonce);
                        }
                        /**
                         * @notice Validates that signature against input data struct
                         * @param signer        Signer's address
                         * @param dataHash      Hash of encoded data struct
                         * @param signature     Signature byte array produced by an EOA wallet or a contract wallet
                         */
                        function _requireValidSignature(
                            address signer,
                            bytes32 dataHash,
                            bytes memory signature
                        ) private view {
                            require(
                                SignatureChecker.isValidSignatureNow(
                                    signer,
                                    MessageHashUtils.toTypedDataHash(_domainSeparator(), dataHash),
                                    signature
                                ),
                                "FiatTokenV2: invalid signature"
                            );
                        }
                        /**
                         * @notice Check that an authorization is unused
                         * @param authorizer    Authorizer's address
                         * @param nonce         Nonce of the authorization
                         */
                        function _requireUnusedAuthorization(address authorizer, bytes32 nonce)
                            private
                            view
                        {
                            require(
                                !_authorizationStates[authorizer][nonce],
                                "FiatTokenV2: authorization is used or canceled"
                            );
                        }
                        /**
                         * @notice Check that authorization is valid
                         * @param authorizer    Authorizer's address
                         * @param nonce         Nonce of the authorization
                         * @param validAfter    The time after which this is valid (unix time)
                         * @param validBefore   The time before which this is valid (unix time)
                         */
                        function _requireValidAuthorization(
                            address authorizer,
                            bytes32 nonce,
                            uint256 validAfter,
                            uint256 validBefore
                        ) private view {
                            require(
                                now > validAfter,
                                "FiatTokenV2: authorization is not yet valid"
                            );
                            require(now < validBefore, "FiatTokenV2: authorization is expired");
                            _requireUnusedAuthorization(authorizer, nonce);
                        }
                        /**
                         * @notice Mark an authorization as used
                         * @param authorizer    Authorizer's address
                         * @param nonce         Nonce of the authorization
                         */
                        function _markAuthorizationAsUsed(address authorizer, bytes32 nonce)
                            private
                        {
                            _authorizationStates[authorizer][nonce] = true;
                            emit AuthorizationUsed(authorizer, nonce);
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { AbstractFiatTokenV2 } from "./AbstractFiatTokenV2.sol";
                    import { EIP712Domain } from "./EIP712Domain.sol";
                    import { MessageHashUtils } from "../util/MessageHashUtils.sol";
                    import { SignatureChecker } from "../util/SignatureChecker.sol";
                    /**
                     * @title EIP-2612
                     * @notice Provide internal implementation for gas-abstracted approvals
                     */
                    abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain {
                        // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
                        bytes32
                            public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                        mapping(address => uint256) private _permitNonces;
                        /**
                         * @notice Nonces for permit
                         * @param owner Token owner's address (Authorizer)
                         * @return Next nonce
                         */
                        function nonces(address owner) external view returns (uint256) {
                            return _permitNonces[owner];
                        }
                        /**
                         * @notice Verify a signed approval permit and execute if valid
                         * @param owner     Token owner's address (Authorizer)
                         * @param spender   Spender's address
                         * @param value     Amount of allowance
                         * @param deadline  The time at which the signature expires (unix time), or max uint256 value to signal no expiration
                         * @param v         v of the signature
                         * @param r         r of the signature
                         * @param s         s of the signature
                         */
                        function _permit(
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) internal {
                            _permit(owner, spender, value, deadline, abi.encodePacked(r, s, v));
                        }
                        /**
                         * @notice Verify a signed approval permit and execute if valid
                         * @dev EOA wallet signatures should be packed in the order of r, s, v.
                         * @param owner      Token owner's address (Authorizer)
                         * @param spender    Spender's address
                         * @param value      Amount of allowance
                         * @param deadline   The time at which the signature expires (unix time), or max uint256 value to signal no expiration
                         * @param signature  Signature byte array signed by an EOA wallet or a contract wallet
                         */
                        function _permit(
                            address owner,
                            address spender,
                            uint256 value,
                            uint256 deadline,
                            bytes memory signature
                        ) internal {
                            require(
                                deadline == type(uint256).max || deadline >= now,
                                "FiatTokenV2: permit is expired"
                            );
                            bytes32 typedDataHash = MessageHashUtils.toTypedDataHash(
                                _domainSeparator(),
                                keccak256(
                                    abi.encode(
                                        PERMIT_TYPEHASH,
                                        owner,
                                        spender,
                                        value,
                                        _permitNonces[owner]++,
                                        deadline
                                    )
                                )
                            );
                            require(
                                SignatureChecker.isValidSignatureNow(
                                    owner,
                                    typedDataHash,
                                    signature
                                ),
                                "EIP2612: invalid signature"
                            );
                            _approve(owner, spender, value);
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { AbstractFiatTokenV1 } from "../v1/AbstractFiatTokenV1.sol";
                    abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 {
                        function _increaseAllowance(
                            address owner,
                            address spender,
                            uint256 increment
                        ) internal virtual;
                        function _decreaseAllowance(
                            address owner,
                            address spender,
                            uint256 decrement
                        ) internal virtual;
                    }
                    /**
                     * SPDX-License-Identifier: MIT
                     *
                     * Copyright (c) 2016 Smart Contract Solutions, Inc.
                     * Copyright (c) 2018-2020 CENTRE SECZ
                     *
                     * Permission is hereby granted, free of charge, to any person obtaining a copy
                     * of this software and associated documentation files (the "Software"), to deal
                     * in the Software without restriction, including without limitation the rights
                     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                     * copies of the Software, and to permit persons to whom the Software is
                     * furnished to do so, subject to the following conditions:
                     *
                     * The above copyright notice and this permission notice shall be included in
                     * copies or substantial portions of the Software.
                     *
                     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                     * SOFTWARE.
                     */
                    pragma solidity 0.6.12;
                    import { Ownable } from "./Ownable.sol";
                    /**
                     * @notice Base contract which allows children to implement an emergency stop
                     * mechanism
                     * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol
                     * Modifications:
                     * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
                     * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
                     * 3. Removed whenPaused (6/14/2018)
                     * 4. Switches ownable library to use ZeppelinOS (7/12/18)
                     * 5. Remove constructor (7/13/18)
                     * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20)
                     * 7. Make public functions external (5/27/20)
                     */
                    contract Pausable is Ownable {
                        event Pause();
                        event Unpause();
                        event PauserChanged(address indexed newAddress);
                        address public pauser;
                        bool public paused = false;
                        /**
                         * @dev Modifier to make a function callable only when the contract is not paused.
                         */
                        modifier whenNotPaused() {
                            require(!paused, "Pausable: paused");
                            _;
                        }
                        /**
                         * @dev throws if called by any account other than the pauser
                         */
                        modifier onlyPauser() {
                            require(msg.sender == pauser, "Pausable: caller is not the pauser");
                            _;
                        }
                        /**
                         * @dev called by the owner to pause, triggers stopped state
                         */
                        function pause() external onlyPauser {
                            paused = true;
                            emit Pause();
                        }
                        /**
                         * @dev called by the owner to unpause, returns to normal state
                         */
                        function unpause() external onlyPauser {
                            paused = false;
                            emit Unpause();
                        }
                        /**
                         * @notice Updates the pauser address.
                         * @param _newPauser The address of the new pauser.
                         */
                        function updatePauser(address _newPauser) external onlyOwner {
                            require(
                                _newPauser != address(0),
                                "Pausable: new pauser is the zero address"
                            );
                            pauser = _newPauser;
                            emit PauserChanged(pauser);
                        }
                    }
                    /**
                     * SPDX-License-Identifier: MIT
                     *
                     * Copyright (c) 2018 zOS Global Limited.
                     * Copyright (c) 2018-2020 CENTRE SECZ
                     *
                     * Permission is hereby granted, free of charge, to any person obtaining a copy
                     * of this software and associated documentation files (the "Software"), to deal
                     * in the Software without restriction, including without limitation the rights
                     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                     * copies of the Software, and to permit persons to whom the Software is
                     * furnished to do so, subject to the following conditions:
                     *
                     * The above copyright notice and this permission notice shall be included in
                     * copies or substantial portions of the Software.
                     *
                     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                     * SOFTWARE.
                     */
                    pragma solidity 0.6.12;
                    /**
                     * @notice The Ownable contract has an owner address, and provides basic
                     * authorization control functions
                     * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol
                     * Modifications:
                     * 1. Consolidate OwnableStorage into this contract (7/13/18)
                     * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
                     * 3. Make public functions external (5/27/20)
                     */
                    contract Ownable {
                        // Owner of the contract
                        address private _owner;
                        /**
                         * @dev Event to show ownership has been transferred
                         * @param previousOwner representing the address of the previous owner
                         * @param newOwner representing the address of the new owner
                         */
                        event OwnershipTransferred(address previousOwner, address newOwner);
                        /**
                         * @dev The constructor sets the original owner of the contract to the sender account.
                         */
                        constructor() public {
                            setOwner(msg.sender);
                        }
                        /**
                         * @dev Tells the address of the owner
                         * @return the address of the owner
                         */
                        function owner() external view returns (address) {
                            return _owner;
                        }
                        /**
                         * @dev Sets a new owner address
                         */
                        function setOwner(address newOwner) internal {
                            _owner = newOwner;
                        }
                        /**
                         * @dev Throws if called by any account other than the owner.
                         */
                        modifier onlyOwner() {
                            require(msg.sender == _owner, "Ownable: caller is not the owner");
                            _;
                        }
                        /**
                         * @dev Allows the current owner to transfer control of the contract to a newOwner.
                         * @param newOwner The address to transfer ownership to.
                         */
                        function transferOwnership(address newOwner) external onlyOwner {
                            require(
                                newOwner != address(0),
                                "Ownable: new owner is the zero address"
                            );
                            emit OwnershipTransferred(_owner, newOwner);
                            setOwner(newOwner);
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
                    import { AbstractFiatTokenV1 } from "./AbstractFiatTokenV1.sol";
                    import { Ownable } from "./Ownable.sol";
                    import { Pausable } from "./Pausable.sol";
                    import { Blacklistable } from "./Blacklistable.sol";
                    /**
                     * @title FiatToken
                     * @dev ERC20 Token backed by fiat reserves
                     */
                    contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable {
                        using SafeMath for uint256;
                        string public name;
                        string public symbol;
                        uint8 public decimals;
                        string public currency;
                        address public masterMinter;
                        bool internal initialized;
                        /// @dev A mapping that stores the balance and blacklist states for a given address.
                        /// The first bit defines whether the address is blacklisted (1 if blacklisted, 0 otherwise).
                        /// The last 255 bits define the balance for the address.
                        mapping(address => uint256) internal balanceAndBlacklistStates;
                        mapping(address => mapping(address => uint256)) internal allowed;
                        uint256 internal totalSupply_ = 0;
                        mapping(address => bool) internal minters;
                        mapping(address => uint256) internal minterAllowed;
                        event Mint(address indexed minter, address indexed to, uint256 amount);
                        event Burn(address indexed burner, uint256 amount);
                        event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
                        event MinterRemoved(address indexed oldMinter);
                        event MasterMinterChanged(address indexed newMasterMinter);
                        /**
                         * @notice Initializes the fiat token contract.
                         * @param tokenName       The name of the fiat token.
                         * @param tokenSymbol     The symbol of the fiat token.
                         * @param tokenCurrency   The fiat currency that the token represents.
                         * @param tokenDecimals   The number of decimals that the token uses.
                         * @param newMasterMinter The masterMinter address for the fiat token.
                         * @param newPauser       The pauser address for the fiat token.
                         * @param newBlacklister  The blacklister address for the fiat token.
                         * @param newOwner        The owner of the fiat token.
                         */
                        function initialize(
                            string memory tokenName,
                            string memory tokenSymbol,
                            string memory tokenCurrency,
                            uint8 tokenDecimals,
                            address newMasterMinter,
                            address newPauser,
                            address newBlacklister,
                            address newOwner
                        ) public {
                            require(!initialized, "FiatToken: contract is already initialized");
                            require(
                                newMasterMinter != address(0),
                                "FiatToken: new masterMinter is the zero address"
                            );
                            require(
                                newPauser != address(0),
                                "FiatToken: new pauser is the zero address"
                            );
                            require(
                                newBlacklister != address(0),
                                "FiatToken: new blacklister is the zero address"
                            );
                            require(
                                newOwner != address(0),
                                "FiatToken: new owner is the zero address"
                            );
                            name = tokenName;
                            symbol = tokenSymbol;
                            currency = tokenCurrency;
                            decimals = tokenDecimals;
                            masterMinter = newMasterMinter;
                            pauser = newPauser;
                            blacklister = newBlacklister;
                            setOwner(newOwner);
                            initialized = true;
                        }
                        /**
                         * @dev Throws if called by any account other than a minter.
                         */
                        modifier onlyMinters() {
                            require(minters[msg.sender], "FiatToken: caller is not a minter");
                            _;
                        }
                        /**
                         * @notice Mints fiat tokens to an address.
                         * @param _to The address that will receive the minted tokens.
                         * @param _amount The amount of tokens to mint. Must be less than or equal
                         * to the minterAllowance of the caller.
                         * @return True if the operation was successful.
                         */
                        function mint(address _to, uint256 _amount)
                            external
                            whenNotPaused
                            onlyMinters
                            notBlacklisted(msg.sender)
                            notBlacklisted(_to)
                            returns (bool)
                        {
                            require(_to != address(0), "FiatToken: mint to the zero address");
                            require(_amount > 0, "FiatToken: mint amount not greater than 0");
                            uint256 mintingAllowedAmount = minterAllowed[msg.sender];
                            require(
                                _amount <= mintingAllowedAmount,
                                "FiatToken: mint amount exceeds minterAllowance"
                            );
                            totalSupply_ = totalSupply_.add(_amount);
                            _setBalance(_to, _balanceOf(_to).add(_amount));
                            minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
                            emit Mint(msg.sender, _to, _amount);
                            emit Transfer(address(0), _to, _amount);
                            return true;
                        }
                        /**
                         * @dev Throws if called by any account other than the masterMinter
                         */
                        modifier onlyMasterMinter() {
                            require(
                                msg.sender == masterMinter,
                                "FiatToken: caller is not the masterMinter"
                            );
                            _;
                        }
                        /**
                         * @notice Gets the minter allowance for an account.
                         * @param minter The address to check.
                         * @return The remaining minter allowance for the account.
                         */
                        function minterAllowance(address minter) external view returns (uint256) {
                            return minterAllowed[minter];
                        }
                        /**
                         * @notice Checks if an account is a minter.
                         * @param account The address to check.
                         * @return True if the account is a minter, false if the account is not a minter.
                         */
                        function isMinter(address account) external view returns (bool) {
                            return minters[account];
                        }
                        /**
                         * @notice Gets the remaining amount of fiat tokens a spender is allowed to transfer on
                         * behalf of the token owner.
                         * @param owner   The token owner's address.
                         * @param spender The spender's address.
                         * @return The remaining allowance.
                         */
                        function allowance(address owner, address spender)
                            external
                            override
                            view
                            returns (uint256)
                        {
                            return allowed[owner][spender];
                        }
                        /**
                         * @notice Gets the totalSupply of the fiat token.
                         * @return The totalSupply of the fiat token.
                         */
                        function totalSupply() external override view returns (uint256) {
                            return totalSupply_;
                        }
                        /**
                         * @notice Gets the fiat token balance of an account.
                         * @param account  The address to check.
                         * @return balance The fiat token balance of the account.
                         */
                        function balanceOf(address account)
                            external
                            override
                            view
                            returns (uint256)
                        {
                            return _balanceOf(account);
                        }
                        /**
                         * @notice Sets a fiat token allowance for a spender to spend on behalf of the caller.
                         * @param spender The spender's address.
                         * @param value   The allowance amount.
                         * @return True if the operation was successful.
                         */
                        function approve(address spender, uint256 value)
                            external
                            virtual
                            override
                            whenNotPaused
                            notBlacklisted(msg.sender)
                            notBlacklisted(spender)
                            returns (bool)
                        {
                            _approve(msg.sender, spender, value);
                            return true;
                        }
                        /**
                         * @dev Internal function to set allowance.
                         * @param owner     Token owner's address.
                         * @param spender   Spender's address.
                         * @param value     Allowance amount.
                         */
                        function _approve(
                            address owner,
                            address spender,
                            uint256 value
                        ) internal override {
                            require(owner != address(0), "ERC20: approve from the zero address");
                            require(spender != address(0), "ERC20: approve to the zero address");
                            allowed[owner][spender] = value;
                            emit Approval(owner, spender, value);
                        }
                        /**
                         * @notice Transfers tokens from an address to another by spending the caller's allowance.
                         * @dev The caller must have some fiat token allowance on the payer's tokens.
                         * @param from  Payer's address.
                         * @param to    Payee's address.
                         * @param value Transfer amount.
                         * @return True if the operation was successful.
                         */
                        function transferFrom(
                            address from,
                            address to,
                            uint256 value
                        )
                            external
                            override
                            whenNotPaused
                            notBlacklisted(msg.sender)
                            notBlacklisted(from)
                            notBlacklisted(to)
                            returns (bool)
                        {
                            require(
                                value <= allowed[from][msg.sender],
                                "ERC20: transfer amount exceeds allowance"
                            );
                            _transfer(from, to, value);
                            allowed[from][msg.sender] = allowed[from][msg.sender].sub(value);
                            return true;
                        }
                        /**
                         * @notice Transfers tokens from the caller.
                         * @param to    Payee's address.
                         * @param value Transfer amount.
                         * @return True if the operation was successful.
                         */
                        function transfer(address to, uint256 value)
                            external
                            override
                            whenNotPaused
                            notBlacklisted(msg.sender)
                            notBlacklisted(to)
                            returns (bool)
                        {
                            _transfer(msg.sender, to, value);
                            return true;
                        }
                        /**
                         * @dev Internal function to process transfers.
                         * @param from  Payer's address.
                         * @param to    Payee's address.
                         * @param value Transfer amount.
                         */
                        function _transfer(
                            address from,
                            address to,
                            uint256 value
                        ) internal override {
                            require(from != address(0), "ERC20: transfer from the zero address");
                            require(to != address(0), "ERC20: transfer to the zero address");
                            require(
                                value <= _balanceOf(from),
                                "ERC20: transfer amount exceeds balance"
                            );
                            _setBalance(from, _balanceOf(from).sub(value));
                            _setBalance(to, _balanceOf(to).add(value));
                            emit Transfer(from, to, value);
                        }
                        /**
                         * @notice Adds or updates a new minter with a mint allowance.
                         * @param minter The address of the minter.
                         * @param minterAllowedAmount The minting amount allowed for the minter.
                         * @return True if the operation was successful.
                         */
                        function configureMinter(address minter, uint256 minterAllowedAmount)
                            external
                            whenNotPaused
                            onlyMasterMinter
                            returns (bool)
                        {
                            minters[minter] = true;
                            minterAllowed[minter] = minterAllowedAmount;
                            emit MinterConfigured(minter, minterAllowedAmount);
                            return true;
                        }
                        /**
                         * @notice Removes a minter.
                         * @param minter The address of the minter to remove.
                         * @return True if the operation was successful.
                         */
                        function removeMinter(address minter)
                            external
                            onlyMasterMinter
                            returns (bool)
                        {
                            minters[minter] = false;
                            minterAllowed[minter] = 0;
                            emit MinterRemoved(minter);
                            return true;
                        }
                        /**
                         * @notice Allows a minter to burn some of its own tokens.
                         * @dev The caller must be a minter, must not be blacklisted, and the amount to burn
                         * should be less than or equal to the account's balance.
                         * @param _amount the amount of tokens to be burned.
                         */
                        function burn(uint256 _amount)
                            external
                            whenNotPaused
                            onlyMinters
                            notBlacklisted(msg.sender)
                        {
                            uint256 balance = _balanceOf(msg.sender);
                            require(_amount > 0, "FiatToken: burn amount not greater than 0");
                            require(balance >= _amount, "FiatToken: burn amount exceeds balance");
                            totalSupply_ = totalSupply_.sub(_amount);
                            _setBalance(msg.sender, balance.sub(_amount));
                            emit Burn(msg.sender, _amount);
                            emit Transfer(msg.sender, address(0), _amount);
                        }
                        /**
                         * @notice Updates the master minter address.
                         * @param _newMasterMinter The address of the new master minter.
                         */
                        function updateMasterMinter(address _newMasterMinter) external onlyOwner {
                            require(
                                _newMasterMinter != address(0),
                                "FiatToken: new masterMinter is the zero address"
                            );
                            masterMinter = _newMasterMinter;
                            emit MasterMinterChanged(masterMinter);
                        }
                        /**
                         * @inheritdoc Blacklistable
                         */
                        function _blacklist(address _account) internal override {
                            _setBlacklistState(_account, true);
                        }
                        /**
                         * @inheritdoc Blacklistable
                         */
                        function _unBlacklist(address _account) internal override {
                            _setBlacklistState(_account, false);
                        }
                        /**
                         * @dev Helper method that sets the blacklist state of an account.
                         * @param _account         The address of the account.
                         * @param _shouldBlacklist True if the account should be blacklisted, false if the account should be unblacklisted.
                         */
                        function _setBlacklistState(address _account, bool _shouldBlacklist)
                            internal
                            virtual
                        {
                            _deprecatedBlacklisted[_account] = _shouldBlacklist;
                        }
                        /**
                         * @dev Helper method that sets the balance of an account.
                         * @param _account The address of the account.
                         * @param _balance The new fiat token balance of the account.
                         */
                        function _setBalance(address _account, uint256 _balance) internal virtual {
                            balanceAndBlacklistStates[_account] = _balance;
                        }
                        /**
                         * @inheritdoc Blacklistable
                         */
                        function _isBlacklisted(address _account)
                            internal
                            virtual
                            override
                            view
                            returns (bool)
                        {
                            return _deprecatedBlacklisted[_account];
                        }
                        /**
                         * @dev Helper method to obtain the balance of an account.
                         * @param _account  The address of the account.
                         * @return          The fiat token balance of the account.
                         */
                        function _balanceOf(address _account)
                            internal
                            virtual
                            view
                            returns (uint256)
                        {
                            return balanceAndBlacklistStates[_account];
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { Ownable } from "./Ownable.sol";
                    /**
                     * @title Blacklistable Token
                     * @dev Allows accounts to be blacklisted by a "blacklister" role
                     */
                    abstract contract Blacklistable is Ownable {
                        address public blacklister;
                        mapping(address => bool) internal _deprecatedBlacklisted;
                        event Blacklisted(address indexed _account);
                        event UnBlacklisted(address indexed _account);
                        event BlacklisterChanged(address indexed newBlacklister);
                        /**
                         * @dev Throws if called by any account other than the blacklister.
                         */
                        modifier onlyBlacklister() {
                            require(
                                msg.sender == blacklister,
                                "Blacklistable: caller is not the blacklister"
                            );
                            _;
                        }
                        /**
                         * @dev Throws if argument account is blacklisted.
                         * @param _account The address to check.
                         */
                        modifier notBlacklisted(address _account) {
                            require(
                                !_isBlacklisted(_account),
                                "Blacklistable: account is blacklisted"
                            );
                            _;
                        }
                        /**
                         * @notice Checks if account is blacklisted.
                         * @param _account The address to check.
                         * @return True if the account is blacklisted, false if the account is not blacklisted.
                         */
                        function isBlacklisted(address _account) external view returns (bool) {
                            return _isBlacklisted(_account);
                        }
                        /**
                         * @notice Adds account to blacklist.
                         * @param _account The address to blacklist.
                         */
                        function blacklist(address _account) external onlyBlacklister {
                            _blacklist(_account);
                            emit Blacklisted(_account);
                        }
                        /**
                         * @notice Removes account from blacklist.
                         * @param _account The address to remove from the blacklist.
                         */
                        function unBlacklist(address _account) external onlyBlacklister {
                            _unBlacklist(_account);
                            emit UnBlacklisted(_account);
                        }
                        /**
                         * @notice Updates the blacklister address.
                         * @param _newBlacklister The address of the new blacklister.
                         */
                        function updateBlacklister(address _newBlacklister) external onlyOwner {
                            require(
                                _newBlacklister != address(0),
                                "Blacklistable: new blacklister is the zero address"
                            );
                            blacklister = _newBlacklister;
                            emit BlacklisterChanged(blacklister);
                        }
                        /**
                         * @dev Checks if account is blacklisted.
                         * @param _account The address to check.
                         * @return true if the account is blacklisted, false otherwise.
                         */
                        function _isBlacklisted(address _account)
                            internal
                            virtual
                            view
                            returns (bool);
                        /**
                         * @dev Helper method that blacklists an account.
                         * @param _account The address to blacklist.
                         */
                        function _blacklist(address _account) internal virtual;
                        /**
                         * @dev Helper method that unblacklists an account.
                         * @param _account The address to unblacklist.
                         */
                        function _unBlacklist(address _account) internal virtual;
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    abstract contract AbstractFiatTokenV1 is IERC20 {
                        function _approve(
                            address owner,
                            address spender,
                            uint256 value
                        ) internal virtual;
                        function _transfer(
                            address from,
                            address to,
                            uint256 value
                        ) internal virtual;
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { Ownable } from "../v1/Ownable.sol";
                    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
                    contract Rescuable is Ownable {
                        using SafeERC20 for IERC20;
                        address private _rescuer;
                        event RescuerChanged(address indexed newRescuer);
                        /**
                         * @notice Returns current rescuer
                         * @return Rescuer's address
                         */
                        function rescuer() external view returns (address) {
                            return _rescuer;
                        }
                        /**
                         * @notice Revert if called by any account other than the rescuer.
                         */
                        modifier onlyRescuer() {
                            require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer");
                            _;
                        }
                        /**
                         * @notice Rescue ERC20 tokens locked up in this contract.
                         * @param tokenContract ERC20 token contract address
                         * @param to        Recipient address
                         * @param amount    Amount to withdraw
                         */
                        function rescueERC20(
                            IERC20 tokenContract,
                            address to,
                            uint256 amount
                        ) external onlyRescuer {
                            tokenContract.safeTransfer(to, amount);
                        }
                        /**
                         * @notice Updates the rescuer address.
                         * @param newRescuer The address of the new rescuer.
                         */
                        function updateRescuer(address newRescuer) external onlyOwner {
                            require(
                                newRescuer != address(0),
                                "Rescuable: new rescuer is the zero address"
                            );
                            _rescuer = newRescuer;
                            emit RescuerChanged(newRescuer);
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { FiatTokenV1 } from "../v1/FiatTokenV1.sol";
                    import { Rescuable } from "./Rescuable.sol";
                    /**
                     * @title FiatTokenV1_1
                     * @dev ERC20 Token backed by fiat reserves
                     */
                    contract FiatTokenV1_1 is FiatTokenV1, Rescuable {
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    import { ECRecover } from "./ECRecover.sol";
                    import { IERC1271 } from "../interface/IERC1271.sol";
                    /**
                     * @dev Signature verification helper that can be used instead of `ECRecover.recover` to seamlessly support both ECDSA
                     * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets.
                     *
                     * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureChecker.sol
                     */
                    library SignatureChecker {
                        /**
                         * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
                         * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECRecover.recover`.
                         * @param signer        Address of the claimed signer
                         * @param digest        Keccak-256 hash digest of the signed message
                         * @param signature     Signature byte array associated with hash
                         */
                        function isValidSignatureNow(
                            address signer,
                            bytes32 digest,
                            bytes memory signature
                        ) external view returns (bool) {
                            if (!isContract(signer)) {
                                return ECRecover.recover(digest, signature) == signer;
                            }
                            return isValidERC1271SignatureNow(signer, digest, signature);
                        }
                        /**
                         * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
                         * against the signer smart contract using ERC1271.
                         * @param signer        Address of the claimed signer
                         * @param digest        Keccak-256 hash digest of the signed message
                         * @param signature     Signature byte array associated with hash
                         *
                         * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
                         * change through time. It could return true at block N and false at block N+1 (or the opposite).
                         */
                        function isValidERC1271SignatureNow(
                            address signer,
                            bytes32 digest,
                            bytes memory signature
                        ) internal view returns (bool) {
                            (bool success, bytes memory result) = signer.staticcall(
                                abi.encodeWithSelector(
                                    IERC1271.isValidSignature.selector,
                                    digest,
                                    signature
                                )
                            );
                            return (success &&
                                result.length >= 32 &&
                                abi.decode(result, (bytes32)) ==
                                bytes32(IERC1271.isValidSignature.selector));
                        }
                        /**
                         * @dev Checks if the input address is a smart contract.
                         */
                        function isContract(address addr) internal view returns (bool) {
                            uint256 size;
                            assembly {
                                size := extcodesize(addr)
                            }
                            return size > 0;
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    /**
                     * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
                     *
                     * The library provides methods for generating a hash of a message that conforms to the
                     * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
                     * specifications.
                     */
                    library MessageHashUtils {
                        /**
                         * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
                         * Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/MessageHashUtils.sol
                         *
                         * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
                         * `\\x19\\x01` and hashing the result. It corresponds to the hash signed by the
                         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
                         *
                         * @param domainSeparator    Domain separator
                         * @param structHash         Hashed EIP-712 data struct
                         * @return digest            The keccak256 digest of an EIP-712 typed data
                         */
                        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash)
                            internal
                            pure
                            returns (bytes32 digest)
                        {
                            assembly {
                                let ptr := mload(0x40)
                                mstore(ptr, "\\x19\\x01")
                                mstore(add(ptr, 0x02), domainSeparator)
                                mstore(add(ptr, 0x22), structHash)
                                digest := keccak256(ptr, 0x42)
                            }
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    /**
                     * @title EIP712
                     * @notice A library that provides EIP712 helper functions
                     */
                    library EIP712 {
                        /**
                         * @notice Make EIP712 domain separator
                         * @param name      Contract name
                         * @param version   Contract version
                         * @param chainId   Blockchain ID
                         * @return Domain separator
                         */
                        function makeDomainSeparator(
                            string memory name,
                            string memory version,
                            uint256 chainId
                        ) internal view returns (bytes32) {
                            return
                                keccak256(
                                    abi.encode(
                                        // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                                        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                                        keccak256(bytes(name)),
                                        keccak256(bytes(version)),
                                        chainId,
                                        address(this)
                                    )
                                );
                        }
                        /**
                         * @notice Make EIP712 domain separator
                         * @param name      Contract name
                         * @param version   Contract version
                         * @return Domain separator
                         */
                        function makeDomainSeparator(string memory name, string memory version)
                            internal
                            view
                            returns (bytes32)
                        {
                            uint256 chainId;
                            assembly {
                                chainId := chainid()
                            }
                            return makeDomainSeparator(name, version, chainId);
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    /**
                     * @title ECRecover
                     * @notice A library that provides a safe ECDSA recovery function
                     */
                    library ECRecover {
                        /**
                         * @notice Recover signer's address from a signed message
                         * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol
                         * Modifications: Accept v, r, and s as separate arguments
                         * @param digest    Keccak-256 hash digest of the signed message
                         * @param v         v of the signature
                         * @param r         r of the signature
                         * @param s         s of the signature
                         * @return Signer address
                         */
                        function recover(
                            bytes32 digest,
                            uint8 v,
                            bytes32 r,
                            bytes32 s
                        ) internal pure returns (address) {
                            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                            // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                            //
                            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                            // these malleable signatures as well.
                            if (
                                uint256(s) >
                                0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
                            ) {
                                revert("ECRecover: invalid signature 's' value");
                            }
                            if (v != 27 && v != 28) {
                                revert("ECRecover: invalid signature 'v' value");
                            }
                            // If the signature is valid (and not malleable), return the signer address
                            address signer = ecrecover(digest, v, r, s);
                            require(signer != address(0), "ECRecover: invalid signature");
                            return signer;
                        }
                        /**
                         * @notice Recover signer's address from a signed message
                         * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0053ee040a7ff1dbc39691c9e67a69f564930a88/contracts/utils/cryptography/ECDSA.sol
                         * @param digest    Keccak-256 hash digest of the signed message
                         * @param signature Signature byte array associated with hash
                         * @return Signer address
                         */
                        function recover(bytes32 digest, bytes memory signature)
                            internal
                            pure
                            returns (address)
                        {
                            require(signature.length == 65, "ECRecover: invalid signature length");
                            bytes32 r;
                            bytes32 s;
                            uint8 v;
                            // ecrecover takes the signature parameters, and the only way to get them
                            // currently is to use assembly.
                            /// @solidity memory-safe-assembly
                            assembly {
                                r := mload(add(signature, 0x20))
                                s := mload(add(signature, 0x40))
                                v := byte(0, mload(add(signature, 0x60)))
                            }
                            return recover(digest, v, r, s);
                        }
                    }
                    /**
                     * SPDX-License-Identifier: Apache-2.0
                     *
                     * Copyright (c) 2023, Circle Internet Financial, LLC.
                     *
                     * Licensed under the Apache License, Version 2.0 (the "License");
                     * you may not use this file except in compliance with the License.
                     * You may obtain a copy of the License at
                     *
                     * http://www.apache.org/licenses/LICENSE-2.0
                     *
                     * Unless required by applicable law or agreed to in writing, software
                     * distributed under the License is distributed on an "AS IS" BASIS,
                     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                     * See the License for the specific language governing permissions and
                     * limitations under the License.
                     */
                    pragma solidity 0.6.12;
                    /**
                     * @dev Interface of the ERC1271 standard signature validation method for
                     * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
                     */
                    interface IERC1271 {
                        /**
                         * @dev Should return whether the signature provided is valid for the provided data
                         * @param hash          Hash of the data to be signed
                         * @param signature     Signature byte array associated with the provided data hash
                         * @return magicValue   bytes4 magic value 0x1626ba7e when function passes
                         */
                        function isValidSignature(bytes32 hash, bytes memory signature)
                            external
                            view
                            returns (bytes4 magicValue);
                    }
                    

                    File 11 of 12: InitializableImmutableAdminUpgradeabilityProxy
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    /**
                     * @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.8.10;
                    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 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)
                        }
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    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.8.10;
                    /**
                     * @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.
                       * Will run if no other function in the contract matches the call data.
                       * 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());
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    import {BaseUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
                    /**
                     * @title BaseImmutableAdminUpgradeabilityProxy
                     * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
                     * @notice This contract combines an upgradeability proxy with an authorization
                     * mechanism for administrative tasks.
                     * @dev 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 internal immutable _admin;
                      /**
                       * @dev Constructor.
                       * @param admin The address of the admin
                       */
                      constructor(address admin) {
                        _admin = admin;
                      }
                      modifier ifAdmin() {
                        if (msg.sender == _admin) {
                          _;
                        } else {
                          _fallback();
                        }
                      }
                      /**
                       * @notice Return the admin address
                       * @return The address of the proxy admin.
                       */
                      function admin() external ifAdmin returns (address) {
                        return _admin;
                      }
                      /**
                       * @notice Return the implementation address
                       * @return The address of the implementation.
                       */
                      function implementation() external ifAdmin returns (address) {
                        return _implementation();
                      }
                      /**
                       * @notice Upgrade the backing implementation of the proxy.
                       * @dev Only the admin can call this function.
                       * @param newImplementation The address of the new implementation.
                       */
                      function upgradeTo(address newImplementation) external ifAdmin {
                        _upgradeTo(newImplementation);
                      }
                      /**
                       * @notice Upgrade the backing implementation of the proxy and call a function
                       * on the new implementation.
                       * @dev This is useful to initialize the proxied contract.
                       * @param newImplementation The 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);
                      }
                      /**
                       * @notice 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.8.10;
                    import {InitializableUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
                    import {Proxy} from '../../../dependencies/openzeppelin/upgradeability/Proxy.sol';
                    import {BaseImmutableAdminUpgradeabilityProxy} from './BaseImmutableAdminUpgradeabilityProxy.sol';
                    /**
                     * @title InitializableAdminUpgradeabilityProxy
                     * @author Aave
                     * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
                     */
                    contract InitializableImmutableAdminUpgradeabilityProxy is
                      BaseImmutableAdminUpgradeabilityProxy,
                      InitializableUpgradeabilityProxy
                    {
                      /**
                       * @dev Constructor.
                       * @param admin The address of the admin
                       */
                      constructor(address admin) BaseImmutableAdminUpgradeabilityProxy(admin) {
                        // Intentionally left blank
                      }
                      /// @inheritdoc BaseImmutableAdminUpgradeabilityProxy
                      function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                        BaseImmutableAdminUpgradeabilityProxy._willFallback();
                      }
                    }
                    

                    File 12 of 12: RewardsController
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    /**
                     * @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.8.10;
                    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: MIT
                    // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                    pragma solidity 0.8.10;
                    /**
                     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                     * checks.
                     *
                     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                     * easily result in undesired exploitation or bugs, since developers usually
                     * assume that overflows raise errors. `SafeCast` restores this intuition by
                     * reverting the transaction when such 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.
                     *
                     * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                     * all math on `uint256` and `int256` and then downcasting.
                     */
                    library SafeCast {
                      /**
                       * @dev Returns the downcasted uint224 from uint256, reverting on
                       * overflow (when the input is greater than largest uint224).
                       *
                       * Counterpart to Solidity's `uint224` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 224 bits
                       */
                      function toUint224(uint256 value) internal pure returns (uint224) {
                        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                        return uint224(value);
                      }
                      /**
                       * @dev Returns the downcasted uint128 from uint256, reverting on
                       * overflow (when the input is greater than largest uint128).
                       *
                       * Counterpart to Solidity's `uint128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       */
                      function toUint128(uint256 value) internal pure returns (uint128) {
                        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                        return uint128(value);
                      }
                      /**
                       * @dev Returns the downcasted uint96 from uint256, reverting on
                       * overflow (when the input is greater than largest uint96).
                       *
                       * Counterpart to Solidity's `uint96` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 96 bits
                       */
                      function toUint96(uint256 value) internal pure returns (uint96) {
                        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                        return uint96(value);
                      }
                      /**
                       * @dev Returns the downcasted uint64 from uint256, reverting on
                       * overflow (when the input is greater than largest uint64).
                       *
                       * Counterpart to Solidity's `uint64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       */
                      function toUint64(uint256 value) internal pure returns (uint64) {
                        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                        return uint64(value);
                      }
                      /**
                       * @dev Returns the downcasted uint32 from uint256, reverting on
                       * overflow (when the input is greater than largest uint32).
                       *
                       * Counterpart to Solidity's `uint32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       */
                      function toUint32(uint256 value) internal pure returns (uint32) {
                        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                        return uint32(value);
                      }
                      /**
                       * @dev Returns the downcasted uint16 from uint256, reverting on
                       * overflow (when the input is greater than largest uint16).
                       *
                       * Counterpart to Solidity's `uint16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       */
                      function toUint16(uint256 value) internal pure returns (uint16) {
                        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                        return uint16(value);
                      }
                      /**
                       * @dev Returns the downcasted uint8 from uint256, reverting on
                       * overflow (when the input is greater than largest uint8).
                       *
                       * Counterpart to Solidity's `uint8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       */
                      function toUint8(uint256 value) internal pure returns (uint8) {
                        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                        return uint8(value);
                      }
                      /**
                       * @dev Converts a signed int256 into an unsigned uint256.
                       *
                       * Requirements:
                       *
                       * - input must be greater than or equal to 0.
                       */
                      function toUint256(int256 value) internal pure returns (uint256) {
                        require(value >= 0, 'SafeCast: value must be positive');
                        return uint256(value);
                      }
                      /**
                       * @dev Returns the downcasted int128 from int256, reverting on
                       * overflow (when the input is less than smallest int128 or
                       * greater than largest int128).
                       *
                       * Counterpart to Solidity's `int128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt128(int256 value) internal pure returns (int128) {
                        require(
                          value >= type(int128).min && value <= type(int128).max,
                          "SafeCast: value doesn't fit in 128 bits"
                        );
                        return int128(value);
                      }
                      /**
                       * @dev Returns the downcasted int64 from int256, reverting on
                       * overflow (when the input is less than smallest int64 or
                       * greater than largest int64).
                       *
                       * Counterpart to Solidity's `int64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt64(int256 value) internal pure returns (int64) {
                        require(
                          value >= type(int64).min && value <= type(int64).max,
                          "SafeCast: value doesn't fit in 64 bits"
                        );
                        return int64(value);
                      }
                      /**
                       * @dev Returns the downcasted int32 from int256, reverting on
                       * overflow (when the input is less than smallest int32 or
                       * greater than largest int32).
                       *
                       * Counterpart to Solidity's `int32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt32(int256 value) internal pure returns (int32) {
                        require(
                          value >= type(int32).min && value <= type(int32).max,
                          "SafeCast: value doesn't fit in 32 bits"
                        );
                        return int32(value);
                      }
                      /**
                       * @dev Returns the downcasted int16 from int256, reverting on
                       * overflow (when the input is less than smallest int16 or
                       * greater than largest int16).
                       *
                       * Counterpart to Solidity's `int16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt16(int256 value) internal pure returns (int16) {
                        require(
                          value >= type(int16).min && value <= type(int16).max,
                          "SafeCast: value doesn't fit in 16 bits"
                        );
                        return int16(value);
                      }
                      /**
                       * @dev Returns the downcasted int8 from int256, reverting on
                       * overflow (when the input is less than smallest int8 or
                       * greater than largest int8).
                       *
                       * Counterpart to Solidity's `int8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits.
                       *
                       * _Available since v3.1._
                       */
                      function toInt8(int256 value) internal pure returns (int8) {
                        require(
                          value >= type(int8).min && value <= type(int8).max,
                          "SafeCast: value doesn't fit in 8 bits"
                        );
                        return int8(value);
                      }
                      /**
                       * @dev Converts an unsigned uint256 into a signed int256.
                       *
                       * Requirements:
                       *
                       * - input must be less than or equal to maxInt256.
                       */
                      function toInt256(uint256 value) internal pure returns (int256) {
                        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                        return int256(value);
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.0;
                    /**
                     * @title IScaledBalanceToken
                     * @author Aave
                     * @notice Defines the basic interface for a scaled-balance token.
                     */
                    interface IScaledBalanceToken {
                      /**
                       * @dev Emitted after the mint action
                       * @param caller The address performing the mint
                       * @param onBehalfOf The address of the user that will receive the minted tokens
                       * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
                       * @param index The next liquidity index of the reserve
                       */
                      event Mint(
                        address indexed caller,
                        address indexed onBehalfOf,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @dev Emitted after the burn action
                       * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
                       * @param from The address from which the tokens will be burned
                       * @param target The address that will receive the underlying, if any
                       * @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
                       * @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
                       * @param index The next liquidity index of the reserve
                       */
                      event Burn(
                        address indexed from,
                        address indexed target,
                        uint256 value,
                        uint256 balanceIncrease,
                        uint256 index
                      );
                      /**
                       * @notice Returns the scaled balance of the user.
                       * @dev 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);
                      /**
                       * @notice 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 total supply
                       */
                      function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                      /**
                       * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
                       * @return The scaled total supply
                       */
                      function scaledTotalSupply() external view returns (uint256);
                      /**
                       * @notice Returns last index interest was accrued to the user's balance
                       * @param user The address of the user
                       * @return The last index interest was accrued to the user's balance, expressed in ray
                       */
                      function getPreviousIndex(address user) external view returns (uint256);
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity 0.8.10;
                    /**
                     * @title VersionedInitializable
                     * @author Aave, inspired by the OpenZeppelin Initializable contract
                     * @notice Helper contract to implement initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * @dev 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.
                     */
                    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;
                        }
                      }
                      /**
                       * @notice Returns the revision number of the contract
                       * @dev Needs to be defined in the inherited class as a constant.
                       * @return The revision number
                       */
                      function getRevision() internal pure virtual returns (uint256);
                      /**
                       * @notice Returns true if and only if the function is running in the constructor
                       * @return True 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.8.10;
                    interface IEACAggregatorProxy {
                      function decimals() external view returns (uint8);
                      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: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {VersionedInitializable} from '@aave/core-v3/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol';
                    import {SafeCast} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {IScaledBalanceToken} from '@aave/core-v3/contracts/interfaces/IScaledBalanceToken.sol';
                    import {RewardsDistributor} from './RewardsDistributor.sol';
                    import {IRewardsController} from './interfaces/IRewardsController.sol';
                    import {ITransferStrategyBase} from './interfaces/ITransferStrategyBase.sol';
                    import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
                    import {IEACAggregatorProxy} from '../misc/interfaces/IEACAggregatorProxy.sol';
                    /**
                     * @title RewardsController
                     * @notice Abstract contract template to build Distributors contracts for ERC20 rewards to protocol participants
                     * @author Aave
                     **/
                    contract RewardsController is RewardsDistributor, VersionedInitializable, IRewardsController {
                      using SafeCast for uint256;
                      uint256 public constant REVISION = 1;
                      // This mapping allows whitelisted addresses to claim on behalf of others
                      // useful for contracts that hold tokens to be rewarded but don't have any native logic to claim Liquidity Mining rewards
                      mapping(address => address) internal _authorizedClaimers;
                      // reward => transfer strategy implementation contract
                      // The TransferStrategy contract abstracts the logic regarding
                      // the source of the reward and how to transfer it to the user.
                      mapping(address => ITransferStrategyBase) internal _transferStrategy;
                      // This mapping contains the price oracle per reward.
                      // A price oracle is enforced for integrators to be able to show incentives at
                      // the current Aave UI without the need to setup an external price registry
                      // At the moment of reward configuration, the Incentives Controller performs
                      // a check to see if the provided reward oracle contains `latestAnswer`.
                      mapping(address => IEACAggregatorProxy) internal _rewardOracle;
                      modifier onlyAuthorizedClaimers(address claimer, address user) {
                        require(_authorizedClaimers[user] == claimer, 'CLAIMER_UNAUTHORIZED');
                        _;
                      }
                      constructor(address emissionManager) RewardsDistributor(emissionManager) {}
                      /**
                       * @dev Initialize for RewardsController
                       * @dev It expects an address as argument since its initialized via PoolAddressesProvider._updateImpl()
                       **/
                      function initialize(address) external initializer {}
                      /// @inheritdoc IRewardsController
                      function getClaimer(address user) external view override returns (address) {
                        return _authorizedClaimers[user];
                      }
                      /**
                       * @dev Returns the revision of the implementation contract
                       * @return uint256, current revision version
                       */
                      function getRevision() internal pure override returns (uint256) {
                        return REVISION;
                      }
                      /// @inheritdoc IRewardsController
                      function getRewardOracle(address reward) external view override returns (address) {
                        return address(_rewardOracle[reward]);
                      }
                      /// @inheritdoc IRewardsController
                      function getTransferStrategy(address reward) external view override returns (address) {
                        return address(_transferStrategy[reward]);
                      }
                      /// @inheritdoc IRewardsController
                      function configureAssets(RewardsDataTypes.RewardsConfigInput[] memory config)
                        external
                        override
                        onlyEmissionManager
                      {
                        for (uint256 i = 0; i < config.length; i++) {
                          // Get the current Scaled Total Supply of AToken or Debt token
                          config[i].totalSupply = IScaledBalanceToken(config[i].asset).scaledTotalSupply();
                          // Install TransferStrategy logic at IncentivesController
                          _installTransferStrategy(config[i].reward, config[i].transferStrategy);
                          // Set reward oracle, enforces input oracle to have latestPrice function
                          _setRewardOracle(config[i].reward, config[i].rewardOracle);
                        }
                        _configureAssets(config);
                      }
                      /// @inheritdoc IRewardsController
                      function setTransferStrategy(address reward, ITransferStrategyBase transferStrategy)
                        external
                        onlyEmissionManager
                      {
                        _installTransferStrategy(reward, transferStrategy);
                      }
                      /// @inheritdoc IRewardsController
                      function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle)
                        external
                        onlyEmissionManager
                      {
                        _setRewardOracle(reward, rewardOracle);
                      }
                      /// @inheritdoc IRewardsController
                      function handleAction(
                        address user,
                        uint256 totalSupply,
                        uint256 userBalance
                      ) external override {
                        _updateData(msg.sender, user, userBalance, totalSupply);
                      }
                      /// @inheritdoc IRewardsController
                      function claimRewards(
                        address[] calldata assets,
                        uint256 amount,
                        address to,
                        address reward
                      ) external override returns (uint256) {
                        require(to != address(0), 'INVALID_TO_ADDRESS');
                        return _claimRewards(assets, amount, msg.sender, msg.sender, to, reward);
                      }
                      /// @inheritdoc IRewardsController
                      function claimRewardsOnBehalf(
                        address[] calldata assets,
                        uint256 amount,
                        address user,
                        address to,
                        address reward
                      ) external override onlyAuthorizedClaimers(msg.sender, user) returns (uint256) {
                        require(user != address(0), 'INVALID_USER_ADDRESS');
                        require(to != address(0), 'INVALID_TO_ADDRESS');
                        return _claimRewards(assets, amount, msg.sender, user, to, reward);
                      }
                      /// @inheritdoc IRewardsController
                      function claimRewardsToSelf(
                        address[] calldata assets,
                        uint256 amount,
                        address reward
                      ) external override returns (uint256) {
                        return _claimRewards(assets, amount, msg.sender, msg.sender, msg.sender, reward);
                      }
                      /// @inheritdoc IRewardsController
                      function claimAllRewards(address[] calldata assets, address to)
                        external
                        override
                        returns (address[] memory rewardsList, uint256[] memory claimedAmounts)
                      {
                        require(to != address(0), 'INVALID_TO_ADDRESS');
                        return _claimAllRewards(assets, msg.sender, msg.sender, to);
                      }
                      /// @inheritdoc IRewardsController
                      function claimAllRewardsOnBehalf(
                        address[] calldata assets,
                        address user,
                        address to
                      )
                        external
                        override
                        onlyAuthorizedClaimers(msg.sender, user)
                        returns (address[] memory rewardsList, uint256[] memory claimedAmounts)
                      {
                        require(user != address(0), 'INVALID_USER_ADDRESS');
                        require(to != address(0), 'INVALID_TO_ADDRESS');
                        return _claimAllRewards(assets, msg.sender, user, to);
                      }
                      /// @inheritdoc IRewardsController
                      function claimAllRewardsToSelf(address[] calldata assets)
                        external
                        override
                        returns (address[] memory rewardsList, uint256[] memory claimedAmounts)
                      {
                        return _claimAllRewards(assets, msg.sender, msg.sender, msg.sender);
                      }
                      /// @inheritdoc IRewardsController
                      function setClaimer(address user, address caller) external override onlyEmissionManager {
                        _authorizedClaimers[user] = caller;
                        emit ClaimerSet(user, caller);
                      }
                      /**
                       * @dev Get user balances and total supply of all the assets specified by the assets parameter
                       * @param assets List of assets to retrieve user balance and total supply
                       * @param user Address of the user
                       * @return userAssetBalances contains a list of structs with user balance and total supply of the given assets
                       */
                      function _getUserAssetBalances(address[] calldata assets, address user)
                        internal
                        view
                        override
                        returns (RewardsDataTypes.UserAssetBalance[] memory userAssetBalances)
                      {
                        userAssetBalances = new RewardsDataTypes.UserAssetBalance[](assets.length);
                        for (uint256 i = 0; i < assets.length; i++) {
                          userAssetBalances[i].asset = assets[i];
                          (userAssetBalances[i].userBalance, userAssetBalances[i].totalSupply) = IScaledBalanceToken(
                            assets[i]
                          ).getScaledUserBalanceAndSupply(user);
                        }
                        return userAssetBalances;
                      }
                      /**
                       * @dev Claims one type of reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards.
                       * @param assets List of assets to check eligible distributions before claiming rewards
                       * @param amount Amount of rewards to claim
                       * @param claimer Address of the claimer who claims rewards on behalf of user
                       * @param user Address to check and claim rewards
                       * @param to Address that will be receiving the rewards
                       * @param reward Address of the reward token
                       * @return Rewards claimed
                       **/
                      function _claimRewards(
                        address[] calldata assets,
                        uint256 amount,
                        address claimer,
                        address user,
                        address to,
                        address reward
                      ) internal returns (uint256) {
                        if (amount == 0) {
                          return 0;
                        }
                        uint256 totalRewards;
                        _updateDataMultiple(user, _getUserAssetBalances(assets, user));
                        for (uint256 i = 0; i < assets.length; i++) {
                          address asset = assets[i];
                          totalRewards += _assets[asset].rewards[reward].usersData[user].accrued;
                          if (totalRewards <= amount) {
                            _assets[asset].rewards[reward].usersData[user].accrued = 0;
                          } else {
                            uint256 difference = totalRewards - amount;
                            totalRewards -= difference;
                            _assets[asset].rewards[reward].usersData[user].accrued = difference.toUint128();
                            break;
                          }
                        }
                        if (totalRewards == 0) {
                          return 0;
                        }
                        _transferRewards(to, reward, totalRewards);
                        emit RewardsClaimed(user, reward, to, claimer, totalRewards);
                        return totalRewards;
                      }
                      /**
                       * @dev Claims one type of reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards.
                       * @param assets List of assets to check eligible distributions before claiming rewards
                       * @param claimer Address of the claimer on behalf of user
                       * @param user Address to check and claim rewards
                       * @param to Address that will be receiving the rewards
                       * @return
                       *   rewardsList List of reward addresses
                       *   claimedAmount List of claimed amounts, follows "rewardsList" items order
                       **/
                      function _claimAllRewards(
                        address[] calldata assets,
                        address claimer,
                        address user,
                        address to
                      ) internal returns (address[] memory rewardsList, uint256[] memory claimedAmounts) {
                        uint256 rewardsListLength = _rewardsList.length;
                        rewardsList = new address[](rewardsListLength);
                        claimedAmounts = new uint256[](rewardsListLength);
                        _updateDataMultiple(user, _getUserAssetBalances(assets, user));
                        for (uint256 i = 0; i < assets.length; i++) {
                          address asset = assets[i];
                          for (uint256 j = 0; j < rewardsListLength; j++) {
                            if (rewardsList[j] == address(0)) {
                              rewardsList[j] = _rewardsList[j];
                            }
                            uint256 rewardAmount = _assets[asset].rewards[rewardsList[j]].usersData[user].accrued;
                            if (rewardAmount != 0) {
                              claimedAmounts[j] += rewardAmount;
                              _assets[asset].rewards[rewardsList[j]].usersData[user].accrued = 0;
                            }
                          }
                        }
                        for (uint256 i = 0; i < rewardsListLength; i++) {
                          _transferRewards(to, rewardsList[i], claimedAmounts[i]);
                          emit RewardsClaimed(user, rewardsList[i], to, claimer, claimedAmounts[i]);
                        }
                        return (rewardsList, claimedAmounts);
                      }
                      /**
                       * @dev Function to transfer rewards to the desired account using delegatecall and
                       * @param to Account address to send the rewards
                       * @param reward Address of the reward token
                       * @param amount Amount of rewards to transfer
                       */
                      function _transferRewards(
                        address to,
                        address reward,
                        uint256 amount
                      ) internal {
                        ITransferStrategyBase transferStrategy = _transferStrategy[reward];
                        bool success = transferStrategy.performTransfer(to, reward, amount);
                        require(success == true, 'TRANSFER_ERROR');
                      }
                      /**
                       * @dev Returns true if `account` is a contract.
                       * @param account The address of the account
                       * @return bool, true if contract, false otherwise
                       */
                      function _isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize, which returns 0 for contracts in
                        // construction, since the code is only stored at the end of the
                        // constructor execution.
                        uint256 size;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                          size := extcodesize(account)
                        }
                        return size > 0;
                      }
                      /**
                       * @dev Internal function to call the optional install hook at the TransferStrategy
                       * @param reward The address of the reward token
                       * @param transferStrategy The address of the reward TransferStrategy
                       */
                      function _installTransferStrategy(address reward, ITransferStrategyBase transferStrategy)
                        internal
                      {
                        require(address(transferStrategy) != address(0), 'STRATEGY_CAN_NOT_BE_ZERO');
                        require(_isContract(address(transferStrategy)) == true, 'STRATEGY_MUST_BE_CONTRACT');
                        _transferStrategy[reward] = transferStrategy;
                        emit TransferStrategyInstalled(reward, address(transferStrategy));
                      }
                      /**
                       * @dev Update the Price Oracle of a reward token. The Price Oracle must follow Chainlink IEACAggregatorProxy interface.
                       * @notice The Price Oracle of a reward is used for displaying correct data about the incentives at the UI frontend.
                       * @param reward The address of the reward token
                       * @param rewardOracle The address of the price oracle
                       */
                      function _setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) internal {
                        require(rewardOracle.latestAnswer() > 0, 'ORACLE_MUST_RETURN_PRICE');
                        _rewardOracle[reward] = rewardOracle;
                        emit RewardOracleUpdated(reward, address(rewardOracle));
                      }
                    }
                    // SPDX-License-Identifier: BUSL-1.1
                    pragma solidity ^0.8.10;
                    import {IScaledBalanceToken} from '@aave/core-v3/contracts/interfaces/IScaledBalanceToken.sol';
                    import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                    import {SafeCast} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol';
                    import {IRewardsDistributor} from './interfaces/IRewardsDistributor.sol';
                    import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
                    /**
                     * @title RewardsDistributor
                     * @notice Accounting contract to manage multiple staking distributions with multiple rewards
                     * @author Aave
                     **/
                    abstract contract RewardsDistributor is IRewardsDistributor {
                      using SafeCast for uint256;
                      // Manager of incentives
                      address public immutable EMISSION_MANAGER;
                      // Deprecated: This storage slot is kept for backwards compatibility purposes.
                      address internal _emissionManager;
                      // Map of rewarded asset addresses and their data (assetAddress => assetData)
                      mapping(address => RewardsDataTypes.AssetData) internal _assets;
                      // Map of reward assets (rewardAddress => enabled)
                      mapping(address => bool) internal _isRewardEnabled;
                      // Rewards list
                      address[] internal _rewardsList;
                      // Assets list
                      address[] internal _assetsList;
                      modifier onlyEmissionManager() {
                        require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER');
                        _;
                      }
                      constructor(address emissionManager) {
                        EMISSION_MANAGER = emissionManager;
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getRewardsData(address asset, address reward)
                        public
                        view
                        override
                        returns (
                          uint256,
                          uint256,
                          uint256,
                          uint256
                        )
                      {
                        return (
                          _assets[asset].rewards[reward].index,
                          _assets[asset].rewards[reward].emissionPerSecond,
                          _assets[asset].rewards[reward].lastUpdateTimestamp,
                          _assets[asset].rewards[reward].distributionEnd
                        );
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getAssetIndex(address asset, address reward)
                        external
                        view
                        override
                        returns (uint256, uint256)
                      {
                        RewardsDataTypes.RewardData storage rewardData = _assets[asset].rewards[reward];
                        return
                          _getAssetIndex(
                            rewardData,
                            IScaledBalanceToken(asset).scaledTotalSupply(),
                            10**_assets[asset].decimals
                          );
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getDistributionEnd(address asset, address reward)
                        external
                        view
                        override
                        returns (uint256)
                      {
                        return _assets[asset].rewards[reward].distributionEnd;
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getRewardsByAsset(address asset) external view override returns (address[] memory) {
                        uint128 rewardsCount = _assets[asset].availableRewardsCount;
                        address[] memory availableRewards = new address[](rewardsCount);
                        for (uint128 i = 0; i < rewardsCount; i++) {
                          availableRewards[i] = _assets[asset].availableRewards[i];
                        }
                        return availableRewards;
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getRewardsList() external view override returns (address[] memory) {
                        return _rewardsList;
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getUserAssetIndex(
                        address user,
                        address asset,
                        address reward
                      ) public view override returns (uint256) {
                        return _assets[asset].rewards[reward].usersData[user].index;
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getUserAccruedRewards(address user, address reward)
                        external
                        view
                        override
                        returns (uint256)
                      {
                        uint256 totalAccrued;
                        for (uint256 i = 0; i < _assetsList.length; i++) {
                          totalAccrued += _assets[_assetsList[i]].rewards[reward].usersData[user].accrued;
                        }
                        return totalAccrued;
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getUserRewards(
                        address[] calldata assets,
                        address user,
                        address reward
                      ) external view override returns (uint256) {
                        return _getUserReward(user, reward, _getUserAssetBalances(assets, user));
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getAllUserRewards(address[] calldata assets, address user)
                        external
                        view
                        override
                        returns (address[] memory rewardsList, uint256[] memory unclaimedAmounts)
                      {
                        RewardsDataTypes.UserAssetBalance[] memory userAssetBalances = _getUserAssetBalances(
                          assets,
                          user
                        );
                        rewardsList = new address[](_rewardsList.length);
                        unclaimedAmounts = new uint256[](rewardsList.length);
                        // Add unrealized rewards from user to unclaimedRewards
                        for (uint256 i = 0; i < userAssetBalances.length; i++) {
                          for (uint256 r = 0; r < rewardsList.length; r++) {
                            rewardsList[r] = _rewardsList[r];
                            unclaimedAmounts[r] += _assets[userAssetBalances[i].asset]
                              .rewards[rewardsList[r]]
                              .usersData[user]
                              .accrued;
                            if (userAssetBalances[i].userBalance == 0) {
                              continue;
                            }
                            unclaimedAmounts[r] += _getPendingRewards(user, rewardsList[r], userAssetBalances[i]);
                          }
                        }
                        return (rewardsList, unclaimedAmounts);
                      }
                      /// @inheritdoc IRewardsDistributor
                      function setDistributionEnd(
                        address asset,
                        address reward,
                        uint32 newDistributionEnd
                      ) external override onlyEmissionManager {
                        uint256 oldDistributionEnd = _assets[asset].rewards[reward].distributionEnd;
                        _assets[asset].rewards[reward].distributionEnd = newDistributionEnd;
                        emit AssetConfigUpdated(
                          asset,
                          reward,
                          _assets[asset].rewards[reward].emissionPerSecond,
                          _assets[asset].rewards[reward].emissionPerSecond,
                          oldDistributionEnd,
                          newDistributionEnd,
                          _assets[asset].rewards[reward].index
                        );
                      }
                      /// @inheritdoc IRewardsDistributor
                      function setEmissionPerSecond(
                        address asset,
                        address[] calldata rewards,
                        uint88[] calldata newEmissionsPerSecond
                      ) external override onlyEmissionManager {
                        require(rewards.length == newEmissionsPerSecond.length, 'INVALID_INPUT');
                        for (uint256 i = 0; i < rewards.length; i++) {
                          RewardsDataTypes.AssetData storage assetConfig = _assets[asset];
                          RewardsDataTypes.RewardData storage rewardConfig = _assets[asset].rewards[rewards[i]];
                          uint256 decimals = assetConfig.decimals;
                          require(
                            decimals != 0 && rewardConfig.lastUpdateTimestamp != 0,
                            'DISTRIBUTION_DOES_NOT_EXIST'
                          );
                          (uint256 newIndex, ) = _updateRewardData(
                            rewardConfig,
                            IScaledBalanceToken(asset).scaledTotalSupply(),
                            10**decimals
                          );
                          uint256 oldEmissionPerSecond = rewardConfig.emissionPerSecond;
                          rewardConfig.emissionPerSecond = newEmissionsPerSecond[i];
                          emit AssetConfigUpdated(
                            asset,
                            rewards[i],
                            oldEmissionPerSecond,
                            newEmissionsPerSecond[i],
                            rewardConfig.distributionEnd,
                            rewardConfig.distributionEnd,
                            newIndex
                          );
                        }
                      }
                      /**
                       * @dev Configure the _assets for a specific emission
                       * @param rewardsInput The array of each asset configuration
                       **/
                      function _configureAssets(RewardsDataTypes.RewardsConfigInput[] memory rewardsInput) internal {
                        for (uint256 i = 0; i < rewardsInput.length; i++) {
                          if (_assets[rewardsInput[i].asset].decimals == 0) {
                            //never initialized before, adding to the list of assets
                            _assetsList.push(rewardsInput[i].asset);
                          }
                          uint256 decimals = _assets[rewardsInput[i].asset].decimals = IERC20Detailed(
                            rewardsInput[i].asset
                          ).decimals();
                          RewardsDataTypes.RewardData storage rewardConfig = _assets[rewardsInput[i].asset].rewards[
                            rewardsInput[i].reward
                          ];
                          // Add reward address to asset available rewards if latestUpdateTimestamp is zero
                          if (rewardConfig.lastUpdateTimestamp == 0) {
                            _assets[rewardsInput[i].asset].availableRewards[
                              _assets[rewardsInput[i].asset].availableRewardsCount
                            ] = rewardsInput[i].reward;
                            _assets[rewardsInput[i].asset].availableRewardsCount++;
                          }
                          // Add reward address to global rewards list if still not enabled
                          if (_isRewardEnabled[rewardsInput[i].reward] == false) {
                            _isRewardEnabled[rewardsInput[i].reward] = true;
                            _rewardsList.push(rewardsInput[i].reward);
                          }
                          // Due emissions is still zero, updates only latestUpdateTimestamp
                          (uint256 newIndex, ) = _updateRewardData(
                            rewardConfig,
                            rewardsInput[i].totalSupply,
                            10**decimals
                          );
                          // Configure emission and distribution end of the reward per asset
                          uint88 oldEmissionsPerSecond = rewardConfig.emissionPerSecond;
                          uint32 oldDistributionEnd = rewardConfig.distributionEnd;
                          rewardConfig.emissionPerSecond = rewardsInput[i].emissionPerSecond;
                          rewardConfig.distributionEnd = rewardsInput[i].distributionEnd;
                          emit AssetConfigUpdated(
                            rewardsInput[i].asset,
                            rewardsInput[i].reward,
                            oldEmissionsPerSecond,
                            rewardsInput[i].emissionPerSecond,
                            oldDistributionEnd,
                            rewardsInput[i].distributionEnd,
                            newIndex
                          );
                        }
                      }
                      /**
                       * @dev Updates the state of the distribution for the specified reward
                       * @param rewardData Storage pointer to the distribution reward config
                       * @param totalSupply Current total of underlying assets for this distribution
                       * @param assetUnit One unit of asset (10**decimals)
                       * @return The new distribution index
                       * @return True if the index was updated, false otherwise
                       **/
                      function _updateRewardData(
                        RewardsDataTypes.RewardData storage rewardData,
                        uint256 totalSupply,
                        uint256 assetUnit
                      ) internal returns (uint256, bool) {
                        (uint256 oldIndex, uint256 newIndex) = _getAssetIndex(rewardData, totalSupply, assetUnit);
                        bool indexUpdated;
                        if (newIndex != oldIndex) {
                          require(newIndex <= type(uint104).max, 'INDEX_OVERFLOW');
                          indexUpdated = true;
                          //optimization: storing one after another saves one SSTORE
                          rewardData.index = uint104(newIndex);
                          rewardData.lastUpdateTimestamp = block.timestamp.toUint32();
                        } else {
                          rewardData.lastUpdateTimestamp = block.timestamp.toUint32();
                        }
                        return (newIndex, indexUpdated);
                      }
                      /**
                       * @dev Updates the state of the distribution for the specific user
                       * @param rewardData Storage pointer to the distribution reward config
                       * @param user The address of the user
                       * @param userBalance The user balance of the asset
                       * @param newAssetIndex The new index of the asset distribution
                       * @param assetUnit One unit of asset (10**decimals)
                       * @return The rewards accrued since the last update
                       **/
                      function _updateUserData(
                        RewardsDataTypes.RewardData storage rewardData,
                        address user,
                        uint256 userBalance,
                        uint256 newAssetIndex,
                        uint256 assetUnit
                      ) internal returns (uint256, bool) {
                        uint256 userIndex = rewardData.usersData[user].index;
                        uint256 rewardsAccrued;
                        bool dataUpdated;
                        if ((dataUpdated = userIndex != newAssetIndex)) {
                          // already checked for overflow in _updateRewardData
                          rewardData.usersData[user].index = uint104(newAssetIndex);
                          if (userBalance != 0) {
                            rewardsAccrued = _getRewards(userBalance, newAssetIndex, userIndex, assetUnit);
                            rewardData.usersData[user].accrued += rewardsAccrued.toUint128();
                          }
                        }
                        return (rewardsAccrued, dataUpdated);
                      }
                      /**
                       * @dev Iterates and accrues all the rewards for asset of the specific user
                       * @param asset The address of the reference asset of the distribution
                       * @param user The user address
                       * @param userBalance The current user asset balance
                       * @param totalSupply Total supply of the asset
                       **/
                      function _updateData(
                        address asset,
                        address user,
                        uint256 userBalance,
                        uint256 totalSupply
                      ) internal {
                        uint256 assetUnit;
                        uint256 numAvailableRewards = _assets[asset].availableRewardsCount;
                        unchecked {
                          assetUnit = 10**_assets[asset].decimals;
                        }
                        if (numAvailableRewards == 0) {
                          return;
                        }
                        unchecked {
                          for (uint128 r = 0; r < numAvailableRewards; r++) {
                            address reward = _assets[asset].availableRewards[r];
                            RewardsDataTypes.RewardData storage rewardData = _assets[asset].rewards[reward];
                            (uint256 newAssetIndex, bool rewardDataUpdated) = _updateRewardData(
                              rewardData,
                              totalSupply,
                              assetUnit
                            );
                            (uint256 rewardsAccrued, bool userDataUpdated) = _updateUserData(
                              rewardData,
                              user,
                              userBalance,
                              newAssetIndex,
                              assetUnit
                            );
                            if (rewardDataUpdated || userDataUpdated) {
                              emit Accrued(asset, reward, user, newAssetIndex, newAssetIndex, rewardsAccrued);
                            }
                          }
                        }
                      }
                      /**
                       * @dev Accrues all the rewards of the assets specified in the userAssetBalances list
                       * @param user The address of the user
                       * @param userAssetBalances List of structs with the user balance and total supply of a set of assets
                       **/
                      function _updateDataMultiple(
                        address user,
                        RewardsDataTypes.UserAssetBalance[] memory userAssetBalances
                      ) internal {
                        for (uint256 i = 0; i < userAssetBalances.length; i++) {
                          _updateData(
                            userAssetBalances[i].asset,
                            user,
                            userAssetBalances[i].userBalance,
                            userAssetBalances[i].totalSupply
                          );
                        }
                      }
                      /**
                       * @dev Return the accrued unclaimed amount of a reward from a user over a list of distribution
                       * @param user The address of the user
                       * @param reward The address of the reward token
                       * @param userAssetBalances List of structs with the user balance and total supply of a set of assets
                       * @return unclaimedRewards The accrued rewards for the user until the moment
                       **/
                      function _getUserReward(
                        address user,
                        address reward,
                        RewardsDataTypes.UserAssetBalance[] memory userAssetBalances
                      ) internal view returns (uint256 unclaimedRewards) {
                        // Add unrealized rewards
                        for (uint256 i = 0; i < userAssetBalances.length; i++) {
                          if (userAssetBalances[i].userBalance == 0) {
                            unclaimedRewards += _assets[userAssetBalances[i].asset]
                              .rewards[reward]
                              .usersData[user]
                              .accrued;
                          } else {
                            unclaimedRewards +=
                              _getPendingRewards(user, reward, userAssetBalances[i]) +
                              _assets[userAssetBalances[i].asset].rewards[reward].usersData[user].accrued;
                          }
                        }
                        return unclaimedRewards;
                      }
                      /**
                       * @dev Calculates the pending (not yet accrued) rewards since the last user action
                       * @param user The address of the user
                       * @param reward The address of the reward token
                       * @param userAssetBalance struct with the user balance and total supply of the incentivized asset
                       * @return The pending rewards for the user since the last user action
                       **/
                      function _getPendingRewards(
                        address user,
                        address reward,
                        RewardsDataTypes.UserAssetBalance memory userAssetBalance
                      ) internal view returns (uint256) {
                        RewardsDataTypes.RewardData storage rewardData = _assets[userAssetBalance.asset].rewards[
                          reward
                        ];
                        uint256 assetUnit = 10**_assets[userAssetBalance.asset].decimals;
                        (, uint256 nextIndex) = _getAssetIndex(rewardData, userAssetBalance.totalSupply, assetUnit);
                        return
                          _getRewards(
                            userAssetBalance.userBalance,
                            nextIndex,
                            rewardData.usersData[user].index,
                            assetUnit
                          );
                      }
                      /**
                       * @dev Internal function for the calculation of user's rewards on a distribution
                       * @param userBalance Balance of the user asset on a distribution
                       * @param reserveIndex Current index of the distribution
                       * @param userIndex Index stored for the user, representation his staking moment
                       * @param assetUnit One unit of asset (10**decimals)
                       * @return The rewards
                       **/
                      function _getRewards(
                        uint256 userBalance,
                        uint256 reserveIndex,
                        uint256 userIndex,
                        uint256 assetUnit
                      ) internal pure returns (uint256) {
                        uint256 result = userBalance * (reserveIndex - userIndex);
                        assembly {
                          result := div(result, assetUnit)
                        }
                        return result;
                      }
                      /**
                       * @dev Calculates the next value of an specific distribution index, with validations
                       * @param rewardData Storage pointer to the distribution reward config
                       * @param totalSupply of the asset being rewarded
                       * @param assetUnit One unit of asset (10**decimals)
                       * @return The new index.
                       **/
                      function _getAssetIndex(
                        RewardsDataTypes.RewardData storage rewardData,
                        uint256 totalSupply,
                        uint256 assetUnit
                      ) internal view returns (uint256, uint256) {
                        uint256 oldIndex = rewardData.index;
                        uint256 distributionEnd = rewardData.distributionEnd;
                        uint256 emissionPerSecond = rewardData.emissionPerSecond;
                        uint256 lastUpdateTimestamp = rewardData.lastUpdateTimestamp;
                        if (
                          emissionPerSecond == 0 ||
                          totalSupply == 0 ||
                          lastUpdateTimestamp == block.timestamp ||
                          lastUpdateTimestamp >= distributionEnd
                        ) {
                          return (oldIndex, oldIndex);
                        }
                        uint256 currentTimestamp = block.timestamp > distributionEnd
                          ? distributionEnd
                          : block.timestamp;
                        uint256 timeDelta = currentTimestamp - lastUpdateTimestamp;
                        uint256 firstTerm = emissionPerSecond * timeDelta * assetUnit;
                        assembly {
                          firstTerm := div(firstTerm, totalSupply)
                        }
                        return (oldIndex, (firstTerm + oldIndex));
                      }
                      /**
                       * @dev Get user balances and total supply of all the assets specified by the assets parameter
                       * @param assets List of assets to retrieve user balance and total supply
                       * @param user Address of the user
                       * @return userAssetBalances contains a list of structs with user balance and total supply of the given assets
                       */
                      function _getUserAssetBalances(address[] calldata assets, address user)
                        internal
                        view
                        virtual
                        returns (RewardsDataTypes.UserAssetBalance[] memory userAssetBalances);
                      /// @inheritdoc IRewardsDistributor
                      function getAssetDecimals(address asset) external view returns (uint8) {
                        return _assets[asset].decimals;
                      }
                      /// @inheritdoc IRewardsDistributor
                      function getEmissionManager() external view returns (address) {
                        return EMISSION_MANAGER;
                      }
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.10;
                    import {IRewardsDistributor} from './IRewardsDistributor.sol';
                    import {ITransferStrategyBase} from './ITransferStrategyBase.sol';
                    import {IEACAggregatorProxy} from '../../misc/interfaces/IEACAggregatorProxy.sol';
                    import {RewardsDataTypes} from '../libraries/RewardsDataTypes.sol';
                    /**
                     * @title IRewardsController
                     * @author Aave
                     * @notice Defines the basic interface for a Rewards Controller.
                     */
                    interface IRewardsController is IRewardsDistributor {
                      /**
                       * @dev Emitted when a new address is whitelisted as claimer of rewards on behalf of a user
                       * @param user The address of the user
                       * @param claimer The address of the claimer
                       */
                      event ClaimerSet(address indexed user, address indexed claimer);
                      /**
                       * @dev Emitted when rewards are claimed
                       * @param user The address of the user rewards has been claimed on behalf of
                       * @param reward The address of the token reward is claimed
                       * @param to The address of the receiver of the rewards
                       * @param claimer The address of the claimer
                       * @param amount The amount of rewards claimed
                       */
                      event RewardsClaimed(
                        address indexed user,
                        address indexed reward,
                        address indexed to,
                        address claimer,
                        uint256 amount
                      );
                      /**
                       * @dev Emitted when a transfer strategy is installed for the reward distribution
                       * @param reward The address of the token reward
                       * @param transferStrategy The address of TransferStrategy contract
                       */
                      event TransferStrategyInstalled(address indexed reward, address indexed transferStrategy);
                      /**
                       * @dev Emitted when the reward oracle is updated
                       * @param reward The address of the token reward
                       * @param rewardOracle The address of oracle
                       */
                      event RewardOracleUpdated(address indexed reward, address indexed rewardOracle);
                      /**
                       * @dev Whitelists an address to claim the rewards on behalf of another address
                       * @param user The address of the user
                       * @param claimer The address of the claimer
                       */
                      function setClaimer(address user, address claimer) external;
                      /**
                       * @dev Sets a TransferStrategy logic contract that determines the logic of the rewards transfer
                       * @param reward The address of the reward token
                       * @param transferStrategy The address of the TransferStrategy logic contract
                       */
                      function setTransferStrategy(address reward, ITransferStrategyBase transferStrategy) external;
                      /**
                       * @dev Sets an Aave Oracle contract to enforce rewards with a source of value.
                       * @notice At the moment of reward configuration, the Incentives Controller performs
                       * a check to see if the reward asset oracle is compatible with IEACAggregator proxy.
                       * This check is enforced for integrators to be able to show incentives at
                       * the current Aave UI without the need to setup an external price registry
                       * @param reward The address of the reward to set the price aggregator
                       * @param rewardOracle The address of price aggregator that follows IEACAggregatorProxy interface
                       */
                      function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) external;
                      /**
                       * @dev Get the price aggregator oracle address
                       * @param reward The address of the reward
                       * @return The price oracle of the reward
                       */
                      function getRewardOracle(address reward) external view returns (address);
                      /**
                       * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)
                       * @param user The address of the user
                       * @return The claimer address
                       */
                      function getClaimer(address user) external view returns (address);
                      /**
                       * @dev Returns the Transfer Strategy implementation contract address being used for a reward address
                       * @param reward The address of the reward
                       * @return The address of the TransferStrategy contract
                       */
                      function getTransferStrategy(address reward) external view returns (address);
                      /**
                       * @dev Configure assets to incentivize with an emission of rewards per second until the end of distribution.
                       * @param config The assets configuration input, the list of structs contains the following fields:
                       *   uint104 emissionPerSecond: The emission per second following rewards unit decimals.
                       *   uint256 totalSupply: The total supply of the asset to incentivize
                       *   uint40 distributionEnd: The end of the distribution of the incentives for an asset
                       *   address asset: The asset address to incentivize
                       *   address reward: The reward token address
                       *   ITransferStrategy transferStrategy: The TransferStrategy address with the install hook and claim logic.
                       *   IEACAggregatorProxy rewardOracle: The Price Oracle of a reward to visualize the incentives at the UI Frontend.
                       *                                     Must follow Chainlink Aggregator IEACAggregatorProxy interface to be compatible.
                       */
                      function configureAssets(RewardsDataTypes.RewardsConfigInput[] memory config) external;
                      /**
                       * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
                       * @dev The units of `totalSupply` and `userBalance` should be the same.
                       * @param user The address of the user whose asset balance has changed
                       * @param totalSupply The total supply of the asset prior to user balance change
                       * @param userBalance The previous user balance prior to balance change
                       **/
                      function handleAction(
                        address user,
                        uint256 totalSupply,
                        uint256 userBalance
                      ) external;
                      /**
                       * @dev Claims reward for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
                       * @param assets List of assets to check eligible distributions before claiming rewards
                       * @param amount The amount of rewards to claim
                       * @param to The address that will be receiving the rewards
                       * @param reward The address of the reward token
                       * @return The amount of rewards claimed
                       **/
                      function claimRewards(
                        address[] calldata assets,
                        uint256 amount,
                        address to,
                        address reward
                      ) external returns (uint256);
                      /**
                       * @dev Claims reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The
                       * caller must be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
                       * @param assets The list of assets to check eligible distributions before claiming rewards
                       * @param amount The amount of rewards to claim
                       * @param user The address to check and claim rewards
                       * @param to The address that will be receiving the rewards
                       * @param reward The address of the reward token
                       * @return The amount of rewards claimed
                       **/
                      function claimRewardsOnBehalf(
                        address[] calldata assets,
                        uint256 amount,
                        address user,
                        address to,
                        address reward
                      ) external returns (uint256);
                      /**
                       * @dev Claims reward for msg.sender, on all the assets of the pool, accumulating the pending rewards
                       * @param assets The list of assets to check eligible distributions before claiming rewards
                       * @param amount The amount of rewards to claim
                       * @param reward The address of the reward token
                       * @return The amount of rewards claimed
                       **/
                      function claimRewardsToSelf(
                        address[] calldata assets,
                        uint256 amount,
                        address reward
                      ) external returns (uint256);
                      /**
                       * @dev Claims all rewards for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
                       * @param assets The list of assets to check eligible distributions before claiming rewards
                       * @param to The address that will be receiving the rewards
                       * @return rewardsList List of addresses of the reward tokens
                       * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardList"
                       **/
                      function claimAllRewards(address[] calldata assets, address to)
                        external
                        returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
                      /**
                       * @dev Claims all rewards for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The caller must
                       * be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
                       * @param assets The list of assets to check eligible distributions before claiming rewards
                       * @param user The address to check and claim rewards
                       * @param to The address that will be receiving the rewards
                       * @return rewardsList List of addresses of the reward tokens
                       * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList"
                       **/
                      function claimAllRewardsOnBehalf(
                        address[] calldata assets,
                        address user,
                        address to
                      ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
                      /**
                       * @dev Claims all reward for msg.sender, on all the assets of the pool, accumulating the pending rewards
                       * @param assets The list of assets to check eligible distributions before claiming rewards
                       * @return rewardsList List of addresses of the reward tokens
                       * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList"
                       **/
                      function claimAllRewardsToSelf(address[] calldata assets)
                        external
                        returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.10;
                    /**
                     * @title IRewardsDistributor
                     * @author Aave
                     * @notice Defines the basic interface for a Rewards Distributor.
                     */
                    interface IRewardsDistributor {
                      /**
                       * @dev Emitted when the configuration of the rewards of an asset is updated.
                       * @param asset The address of the incentivized asset
                       * @param reward The address of the reward token
                       * @param oldEmission The old emissions per second value of the reward distribution
                       * @param newEmission The new emissions per second value of the reward distribution
                       * @param oldDistributionEnd The old end timestamp of the reward distribution
                       * @param newDistributionEnd The new end timestamp of the reward distribution
                       * @param assetIndex The index of the asset distribution
                       */
                      event AssetConfigUpdated(
                        address indexed asset,
                        address indexed reward,
                        uint256 oldEmission,
                        uint256 newEmission,
                        uint256 oldDistributionEnd,
                        uint256 newDistributionEnd,
                        uint256 assetIndex
                      );
                      /**
                       * @dev Emitted when rewards of an asset are accrued on behalf of a user.
                       * @param asset The address of the incentivized asset
                       * @param reward The address of the reward token
                       * @param user The address of the user that rewards are accrued on behalf of
                       * @param assetIndex The index of the asset distribution
                       * @param userIndex The index of the asset distribution on behalf of the user
                       * @param rewardsAccrued The amount of rewards accrued
                       */
                      event Accrued(
                        address indexed asset,
                        address indexed reward,
                        address indexed user,
                        uint256 assetIndex,
                        uint256 userIndex,
                        uint256 rewardsAccrued
                      );
                      /**
                       * @dev Sets the end date for the distribution
                       * @param asset The asset to incentivize
                       * @param reward The reward token that incentives the asset
                       * @param newDistributionEnd The end date of the incentivization, in unix time format
                       **/
                      function setDistributionEnd(
                        address asset,
                        address reward,
                        uint32 newDistributionEnd
                      ) external;
                      /**
                       * @dev Sets the emission per second of a set of reward distributions
                       * @param asset The asset is being incentivized
                       * @param rewards List of reward addresses are being distributed
                       * @param newEmissionsPerSecond List of new reward emissions per second
                       */
                      function setEmissionPerSecond(
                        address asset,
                        address[] calldata rewards,
                        uint88[] calldata newEmissionsPerSecond
                      ) external;
                      /**
                       * @dev Gets the end date for the distribution
                       * @param asset The incentivized asset
                       * @param reward The reward token of the incentivized asset
                       * @return The timestamp with the end of the distribution, in unix time format
                       **/
                      function getDistributionEnd(address asset, address reward) external view returns (uint256);
                      /**
                       * @dev Returns the index of a user on a reward distribution
                       * @param user Address of the user
                       * @param asset The incentivized asset
                       * @param reward The reward token of the incentivized asset
                       * @return The current user asset index, not including new distributions
                       **/
                      function getUserAssetIndex(
                        address user,
                        address asset,
                        address reward
                      ) external view returns (uint256);
                      /**
                       * @dev Returns the configuration of the distribution reward for a certain asset
                       * @param asset The incentivized asset
                       * @param reward The reward token of the incentivized asset
                       * @return The index of the asset distribution
                       * @return The emission per second of the reward distribution
                       * @return The timestamp of the last update of the index
                       * @return The timestamp of the distribution end
                       **/
                      function getRewardsData(address asset, address reward)
                        external
                        view
                        returns (
                          uint256,
                          uint256,
                          uint256,
                          uint256
                        );
                      /**
                       * @dev Calculates the next value of an specific distribution index, with validations.
                       * @param asset The incentivized asset
                       * @param reward The reward token of the incentivized asset
                       * @return The old index of the asset distribution
                       * @return The new index of the asset distribution
                       **/
                      function getAssetIndex(address asset, address reward) external view returns (uint256, uint256);
                      /**
                       * @dev Returns the list of available reward token addresses of an incentivized asset
                       * @param asset The incentivized asset
                       * @return List of rewards addresses of the input asset
                       **/
                      function getRewardsByAsset(address asset) external view returns (address[] memory);
                      /**
                       * @dev Returns the list of available reward addresses
                       * @return List of rewards supported in this contract
                       **/
                      function getRewardsList() external view returns (address[] memory);
                      /**
                       * @dev Returns the accrued rewards balance of a user, not including virtually accrued rewards since last distribution.
                       * @param user The address of the user
                       * @param reward The address of the reward token
                       * @return Unclaimed rewards, not including new distributions
                       **/
                      function getUserAccruedRewards(address user, address reward) external view returns (uint256);
                      /**
                       * @dev Returns a single rewards balance of a user, including virtually accrued and unrealized claimable rewards.
                       * @param assets List of incentivized assets to check eligible distributions
                       * @param user The address of the user
                       * @param reward The address of the reward token
                       * @return The rewards amount
                       **/
                      function getUserRewards(
                        address[] calldata assets,
                        address user,
                        address reward
                      ) external view returns (uint256);
                      /**
                       * @dev Returns a list all rewards of a user, including already accrued and unrealized claimable rewards
                       * @param assets List of incentivized assets to check eligible distributions
                       * @param user The address of the user
                       * @return The list of reward addresses
                       * @return The list of unclaimed amount of rewards
                       **/
                      function getAllUserRewards(address[] calldata assets, address user)
                        external
                        view
                        returns (address[] memory, uint256[] memory);
                      /**
                       * @dev Returns the decimals of an asset to calculate the distribution delta
                       * @param asset The address to retrieve decimals
                       * @return The decimals of an underlying asset
                       */
                      function getAssetDecimals(address asset) external view returns (uint8);
                      /**
                       * @dev Returns the address of the emission manager
                       * @return The address of the EmissionManager
                       */
                      function EMISSION_MANAGER() external view returns (address);
                      /**
                       * @dev Returns the address of the emission manager.
                       * Deprecated: This getter is maintained for compatibility purposes. Use the `EMISSION_MANAGER()` function instead.
                       * @return The address of the EmissionManager
                       */
                      function getEmissionManager() external view returns (address);
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.10;
                    interface ITransferStrategyBase {
                      event EmergencyWithdrawal(
                        address indexed caller,
                        address indexed token,
                        address indexed to,
                        uint256 amount
                      );
                      /**
                       * @dev Perform custom transfer logic via delegate call from source contract to a TransferStrategy implementation
                       * @param to Account to transfer rewards
                       * @param reward Address of the reward token
                       * @param amount Amount to transfer to the "to" address parameter
                       * @return Returns true bool if transfer logic succeeds
                       */
                      function performTransfer(
                        address to,
                        address reward,
                        uint256 amount
                      ) external returns (bool);
                      /**
                       * @return Returns the address of the Incentives Controller
                       */
                      function getIncentivesController() external view returns (address);
                      /**
                       * @return Returns the address of the Rewards admin
                       */
                      function getRewardsAdmin() external view returns (address);
                      /**
                       * @dev Perform an emergency token withdrawal only callable by the Rewards admin
                       * @param token Address of the token to withdraw funds from this contract
                       * @param to Address of the recipient of the withdrawal
                       * @param amount Amount of the withdrawal
                       */
                      function emergencyWithdrawal(
                        address token,
                        address to,
                        uint256 amount
                      ) external;
                    }
                    // SPDX-License-Identifier: AGPL-3.0
                    pragma solidity ^0.8.10;
                    import {ITransferStrategyBase} from '../interfaces/ITransferStrategyBase.sol';
                    import {IEACAggregatorProxy} from '../../misc/interfaces/IEACAggregatorProxy.sol';
                    library RewardsDataTypes {
                      struct RewardsConfigInput {
                        uint88 emissionPerSecond;
                        uint256 totalSupply;
                        uint32 distributionEnd;
                        address asset;
                        address reward;
                        ITransferStrategyBase transferStrategy;
                        IEACAggregatorProxy rewardOracle;
                      }
                      struct UserAssetBalance {
                        address asset;
                        uint256 userBalance;
                        uint256 totalSupply;
                      }
                      struct UserData {
                        // Liquidity index of the reward distribution for the user
                        uint104 index;
                        // Amount of accrued rewards for the user since last user index update
                        uint128 accrued;
                      }
                      struct RewardData {
                        // Liquidity index of the reward distribution
                        uint104 index;
                        // Amount of reward tokens distributed per second
                        uint88 emissionPerSecond;
                        // Timestamp of the last reward index update
                        uint32 lastUpdateTimestamp;
                        // The end of the distribution of rewards (in seconds)
                        uint32 distributionEnd;
                        // Map of user addresses and their rewards data (userAddress => userData)
                        mapping(address => UserData) usersData;
                      }
                      struct AssetData {
                        // Map of reward token addresses and their data (rewardTokenAddress => rewardData)
                        mapping(address => RewardData) rewards;
                        // List of reward token addresses for the asset
                        mapping(uint128 => address) availableRewards;
                        // Count of reward tokens for the asset
                        uint128 availableRewardsCount;
                        // Number of decimals of the asset
                        uint8 decimals;
                      }
                    }