ETH Price: $2,547.11 (+2.40%)

Transaction Decoder

Block:
21426196 at Dec-18-2024 01:40:35 AM +UTC
Transaction Fee:
0.001826927971213475 ETH $4.65
Gas Used:
210,335 Gas / 8.685801085 Gwei

Emitted Events:

131 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x000000000000000000000000657e8c867d8b37dcc18fa4caead9c45eb088c642, 0000000000000000000000000000000000000000000c4b4e57ce43334b9da61e, 0000000000000000000000000000000000000000002c890a6b826d9e49c652c1, 000000000000000000000000000000000000000000196907e8f339a6be32960a, 0000000000000000000000000000000000000000033e72eabd7907297525838c, 00000000000000000000000000000000000000000342cd33176bbdb92498ec3f )
132 InitializableImmutableAdminUpgradeabilityProxy.0x44c58d81365b66dd4b1a7f36c25aa97b8c71c361ee4937adc1a00000227db5dd( 0x44c58d81365b66dd4b1a7f36c25aa97b8c71c361ee4937adc1a00000227db5dd, 0x000000000000000000000000657e8c867d8b37dcc18fa4caead9c45eb088c642, 0x000000000000000000000000f396126d4ce9e8b8f8c70b5173b46834aeef66a2 )
133 InitializableImmutableAdminUpgradeabilityProxy.0x3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe8( 0x3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe8, 0x00000000000000000000000052bb650211e8a6986287306a4c09b73a9affd5e9, 0x0000000000000000000000002da17faf782ae884faf7db2208bbc66b6e085c22, 0x000000000000000000000000f396126d4ce9e8b8f8c70b5173b46834aeef66a2, 0000000000000000000000000000000000000000000f577d97725a699726c591, 0000000000000000000000000000000000000000000f577d97725a699726c591, 000000000000000000000000000000000000000000000032956460989e63c2e6 )
134 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000f396126d4ce9e8b8f8c70b5173b46834aeef66a2, 0x0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000034c0691 )
135 InitializableImmutableAdminUpgradeabilityProxy.0x4cf25bc1d991c17529c25213d3cc0cda295eeaad5f13f361969b12ea48015f90( 0x4cf25bc1d991c17529c25213d3cc0cda295eeaad5f13f361969b12ea48015f90, 0x000000000000000000000000f396126d4ce9e8b8f8c70b5173b46834aeef66a2, 0x000000000000000000000000f396126d4ce9e8b8f8c70b5173b46834aeef66a2, 00000000000000000000000000000000000000000000000000000000034c0691, 00000000000000000000000000000000000000000000000000000000000091b6, 0000000000000000000000000000000000000000033e72eabd7907297525838c )
136 BoringVault.Transfer( from=InitializableImmutableAdminUpgradeabilityProxy, to=[Sender] 0xf396126d4ce9e8b8f8c70b5173b46834aeef66a2, amount=55351367 )
137 InitializableImmutableAdminUpgradeabilityProxy.0x3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f7( 0x3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f7, 0x000000000000000000000000657e8c867d8b37dcc18fa4caead9c45eb088c642, 0x000000000000000000000000f396126d4ce9e8b8f8c70b5173b46834aeef66a2, 0x000000000000000000000000f396126d4ce9e8b8f8c70b5173b46834aeef66a2, 00000000000000000000000000000000000000000000000000000000034c9847 )

Account State Difference:

  Address   Before After State Difference Code
0x52bB6502...A9Affd5e9
0x657e8C86...EB088C642
0x938e23c1...bD9C51d2b
(beaverbuild)
18.447781970014999475 Eth18.447801346075199475 Eth0.0000193760602
0xCD2b3107...7Ee97F987
0xF396126d...4AEeF66A2
0.070295936397874374 Eth
Nonce: 1087
0.068469008426660899 Eth
Nonce: 1088
0.001826927971213475

Execution Trace

InitializableImmutableAdminUpgradeabilityProxy.69328dec( )
  • Pool.withdraw( asset=0x657e8C867D8B37dCC18fA4Caead9C45EB088C642, amount=115792089237316195423570985008687907853269984665640564039457584007913129639935, to=0xF396126d4ce9e8b8f8c70b5173b46834AEeF66A2 ) => ( 55351367 )
    • PoolAddressesProvider.STATICCALL( )
    • SupplyLogic.186dea44( )
      • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
        • VariableDebtToken.DELEGATECALL( )
        • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
          • StableDebtTokenDisabled.DELEGATECALL( )
          • InitializableImmutableAdminUpgradeabilityProxy.1da24f3e( )
            • AToken.scaledBalanceOf( user=0xF396126d4ce9e8b8f8c70b5173b46834AEeF66A2 ) => ( 55133528 )
            • DefaultReserveInterestRateStrategy.calculateInterestRates( params=[{name:unbacked, type:uint256, order:1, indexed:false, value:0, valueString:0}, {name:liquidityAdded, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:liquidityTaken, type:uint256, order:3, indexed:false, value:55351367, valueString:55351367}, {name:totalStableDebt, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:totalVariableDebt, type:uint256, order:5, indexed:false, value:54760107790, valueString:54760107790}, {name:averageStableBorrowRate, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:reserveFactor, type:uint256, order:7, indexed:false, value:3000, valueString:3000}, {name:reserve, type:address, order:8, indexed:false, value:0x657e8C867D8B37dCC18fA4Caead9C45EB088C642, valueString:0x657e8C867D8B37dCC18fA4Caead9C45EB088C642}, {name:aToken, type:address, order:9, indexed:false, value:0x52bB650211e8a6986287306A4c09B73A9Affd5e9, valueString:0x52bB650211e8a6986287306A4c09B73A9Affd5e9}] ) => ( 14862732494696936925668894, 53839892485512397817533121, 30719139884099182540264970 )
              • BoringVault.balanceOf( 0x52bB650211e8a6986287306A4c09B73A9Affd5e9 ) => ( 24522154800 )
              • InitializableImmutableAdminUpgradeabilityProxy.d7020d0a( )
                • AToken.burn( from=0xF396126d4ce9e8b8f8c70b5173b46834AEeF66A2, receiverOfUnderlying=0xF396126d4ce9e8b8f8c70b5173b46834AEeF66A2, amount=55351367, index=1003951110250254222586381196 )
                  • InitializableImmutableAdminUpgradeabilityProxy.31873e2e( )
                    • RewardsController.handleAction( user=0xF396126d4ce9e8b8f8c70b5173b46834AEeF66A2, totalSupply=78967471697, userBalance=55133528 )
                    • BoringVault.transfer( to=0xF396126d4ce9e8b8f8c70b5173b46834AEeF66A2, amount=55351367 ) => ( True )
                      • TellerWithMultiAssetSupport.beforeTransfer( from=0x52bB650211e8a6986287306A4c09B73A9Affd5e9, to=0xF396126d4ce9e8b8f8c70b5173b46834AEeF66A2, operator=0x52bB650211e8a6986287306A4c09B73A9Affd5e9 )
                        File 1 of 15: InitializableImmutableAdminUpgradeabilityProxy
                        // 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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import './Proxy.sol';
                        import '../contracts/Address.sol';
                        /**
                         * @title BaseUpgradeabilityProxy
                         * @dev This contract implements a proxy that allows to change the
                         * implementation address to which it will delegate.
                         * Such a change is called an implementation upgrade.
                         */
                        contract BaseUpgradeabilityProxy is Proxy {
                          /**
                           * @dev Emitted when the implementation is upgraded.
                           * @param implementation Address of the new implementation.
                           */
                          event Upgraded(address indexed implementation);
                          /**
                           * @dev Storage slot with the address of the current implementation.
                           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                           * validated in the constructor.
                           */
                          bytes32 internal constant IMPLEMENTATION_SLOT =
                            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                          /**
                           * @dev Returns the current implementation.
                           * @return impl Address of the current implementation
                           */
                          function _implementation() internal 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.12;
                        import './BaseUpgradeabilityProxy.sol';
                        /**
                         * @title InitializableUpgradeabilityProxy
                         * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                         * implementation and init data.
                         */
                        contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                          /**
                           * @dev Contract initializer.
                           * @param _logic Address of the initial implementation.
                           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                           * It should include the signature and the parameters of the function to be called, as described in
                           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                           */
                          function initialize(address _logic, bytes memory _data) public payable {
                            require(_implementation() == address(0));
                            assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                            _setImplementation(_logic);
                            if (_data.length > 0) {
                              (bool success, ) = _logic.delegatecall(_data);
                              require(success);
                            }
                          }
                        }
                        // SPDX-License-Identifier: AGPL-3.0
                        pragma solidity 0.8.12;
                        /**
                         * @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.12;
                        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.12;
                        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 15: InitializableImmutableAdminUpgradeabilityProxy
                        // 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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import './Proxy.sol';
                        import '../contracts/Address.sol';
                        /**
                         * @title BaseUpgradeabilityProxy
                         * @dev This contract implements a proxy that allows to change the
                         * implementation address to which it will delegate.
                         * Such a change is called an implementation upgrade.
                         */
                        contract BaseUpgradeabilityProxy is Proxy {
                          /**
                           * @dev Emitted when the implementation is upgraded.
                           * @param implementation Address of the new implementation.
                           */
                          event Upgraded(address indexed implementation);
                          /**
                           * @dev Storage slot with the address of the current implementation.
                           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                           * validated in the constructor.
                           */
                          bytes32 internal constant IMPLEMENTATION_SLOT =
                            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                          /**
                           * @dev Returns the current implementation.
                           * @return impl Address of the current implementation
                           */
                          function _implementation() internal 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.12;
                        import './BaseUpgradeabilityProxy.sol';
                        /**
                         * @title InitializableUpgradeabilityProxy
                         * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                         * implementation and init data.
                         */
                        contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                          /**
                           * @dev Contract initializer.
                           * @param _logic Address of the initial implementation.
                           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                           * It should include the signature and the parameters of the function to be called, as described in
                           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                           */
                          function initialize(address _logic, bytes memory _data) public payable {
                            require(_implementation() == address(0));
                            assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                            _setImplementation(_logic);
                            if (_data.length > 0) {
                              (bool success, ) = _logic.delegatecall(_data);
                              require(success);
                            }
                          }
                        }
                        // SPDX-License-Identifier: AGPL-3.0
                        pragma solidity 0.8.12;
                        /**
                         * @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.12;
                        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.12;
                        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 3 of 15: InitializableImmutableAdminUpgradeabilityProxy
                        // 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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import './Proxy.sol';
                        import '../contracts/Address.sol';
                        /**
                         * @title BaseUpgradeabilityProxy
                         * @dev This contract implements a proxy that allows to change the
                         * implementation address to which it will delegate.
                         * Such a change is called an implementation upgrade.
                         */
                        contract BaseUpgradeabilityProxy is Proxy {
                          /**
                           * @dev Emitted when the implementation is upgraded.
                           * @param implementation Address of the new implementation.
                           */
                          event Upgraded(address indexed implementation);
                          /**
                           * @dev Storage slot with the address of the current implementation.
                           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                           * validated in the constructor.
                           */
                          bytes32 internal constant IMPLEMENTATION_SLOT =
                            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                          /**
                           * @dev Returns the current implementation.
                           * @return impl Address of the current implementation
                           */
                          function _implementation() internal 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.12;
                        import './BaseUpgradeabilityProxy.sol';
                        /**
                         * @title InitializableUpgradeabilityProxy
                         * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                         * implementation and init data.
                         */
                        contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                          /**
                           * @dev Contract initializer.
                           * @param _logic Address of the initial implementation.
                           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                           * It should include the signature and the parameters of the function to be called, as described in
                           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                           */
                          function initialize(address _logic, bytes memory _data) public payable {
                            require(_implementation() == address(0));
                            assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                            _setImplementation(_logic);
                            if (_data.length > 0) {
                              (bool success, ) = _logic.delegatecall(_data);
                              require(success);
                            }
                          }
                        }
                        // SPDX-License-Identifier: AGPL-3.0
                        pragma solidity 0.8.12;
                        /**
                         * @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.12;
                        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.12;
                        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 15: BoringVault
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.8.21;
                        import {Address} from "@openzeppelin/contracts/utils/Address.sol";
                        import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
                        import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
                        import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
                        import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
                        import {ERC20} from "@solmate/tokens/ERC20.sol";
                        import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
                        import {Auth, Authority} from "@solmate/auth/Auth.sol";
                        contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
                            using Address for address;
                            using SafeTransferLib for ERC20;
                            using FixedPointMathLib for uint256;
                            // ========================================= STATE =========================================
                            /**
                             * @notice Contract responsbile for implementing `beforeTransfer`.
                             */
                            BeforeTransferHook public hook;
                            //============================== EVENTS ===============================
                            event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
                            event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);
                            //============================== CONSTRUCTOR ===============================
                            constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
                                ERC20(_name, _symbol, _decimals)
                                Auth(_owner, Authority(address(0)))
                            {}
                            //============================== MANAGE ===============================
                            /**
                             * @notice Allows manager to make an arbitrary function call from this contract.
                             * @dev Callable by MANAGER_ROLE.
                             */
                            function manage(address target, bytes calldata data, uint256 value)
                                external
                                requiresAuth
                                returns (bytes memory result)
                            {
                                result = target.functionCallWithValue(data, value);
                            }
                            /**
                             * @notice Allows manager to make arbitrary function calls from this contract.
                             * @dev Callable by MANAGER_ROLE.
                             */
                            function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
                                external
                                requiresAuth
                                returns (bytes[] memory results)
                            {
                                uint256 targetsLength = targets.length;
                                results = new bytes[](targetsLength);
                                for (uint256 i; i < targetsLength; ++i) {
                                    results[i] = targets[i].functionCallWithValue(data[i], values[i]);
                                }
                            }
                            //============================== ENTER ===============================
                            /**
                             * @notice Allows minter to mint shares, in exchange for assets.
                             * @dev If assetAmount is zero, no assets are transferred in.
                             * @dev Callable by MINTER_ROLE.
                             */
                            function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
                                external
                                requiresAuth
                            {
                                // Transfer assets in
                                if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);
                                // Mint shares.
                                _mint(to, shareAmount);
                                emit Enter(from, address(asset), assetAmount, to, shareAmount);
                            }
                            //============================== EXIT ===============================
                            /**
                             * @notice Allows burner to burn shares, in exchange for assets.
                             * @dev If assetAmount is zero, no assets are transferred out.
                             * @dev Callable by BURNER_ROLE.
                             */
                            function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
                                external
                                requiresAuth
                            {
                                // Burn shares.
                                _burn(from, shareAmount);
                                // Transfer assets out.
                                if (assetAmount > 0) asset.safeTransfer(to, assetAmount);
                                emit Exit(to, address(asset), assetAmount, from, shareAmount);
                            }
                            //============================== BEFORE TRANSFER HOOK ===============================
                            /**
                             * @notice Sets the share locker.
                             * @notice If set to zero address, the share locker logic is disabled.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function setBeforeTransferHook(address _hook) external requiresAuth {
                                hook = BeforeTransferHook(_hook);
                            }
                            /**
                             * @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
                             */
                            function _callBeforeTransfer(address from, address to) internal view {
                                if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
                            }
                            function transfer(address to, uint256 amount) public override returns (bool) {
                                _callBeforeTransfer(msg.sender, to);
                                return super.transfer(to, amount);
                            }
                            function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
                                _callBeforeTransfer(from, to);
                                return super.transferFrom(from, to, amount);
                            }
                            //============================== RECEIVE ===============================
                            receive() external payable {}
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
                        pragma solidity ^0.8.20;
                        /**
                         * @dev Collection of functions related to the address type
                         */
                        library Address {
                            /**
                             * @dev The ETH balance of the account is not enough to perform the operation.
                             */
                            error AddressInsufficientBalance(address account);
                            /**
                             * @dev There's no code at `target` (it is not a contract).
                             */
                            error AddressEmptyCode(address target);
                            /**
                             * @dev A call to an address target failed. The target may have reverted.
                             */
                            error FailedInnerCall();
                            /**
                             * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                             */
                            function sendValue(address payable recipient, uint256 amount) internal {
                                if (address(this).balance < amount) {
                                    revert AddressInsufficientBalance(address(this));
                                }
                                (bool success, ) = recipient.call{value: amount}("");
                                if (!success) {
                                    revert FailedInnerCall();
                                }
                            }
                            /**
                             * @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 or custom error, it is bubbled
                             * up by this function (like regular Solidity function calls). However, if
                             * the call reverted with no returned reason, this function reverts with a
                             * {FailedInnerCall} error.
                             *
                             * 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.
                             */
                            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                return functionCallWithValue(target, data, 0);
                            }
                            /**
                             * @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`.
                             */
                            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                                if (address(this).balance < value) {
                                    revert AddressInsufficientBalance(address(this));
                                }
                                (bool success, bytes memory returndata) = target.call{value: value}(data);
                                return verifyCallResultFromTarget(target, success, returndata);
                            }
                            /**
                             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                             * but performing a static call.
                             */
                            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return verifyCallResultFromTarget(target, success, returndata);
                            }
                            /**
                             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                             * but performing a delegate call.
                             */
                            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return verifyCallResultFromTarget(target, success, returndata);
                            }
                            /**
                             * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                             * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
                             * unsuccessful call.
                             */
                            function verifyCallResultFromTarget(
                                address target,
                                bool success,
                                bytes memory returndata
                            ) internal view returns (bytes memory) {
                                if (!success) {
                                    _revert(returndata);
                                } else {
                                    // only check if target is a contract if the call was successful and the return data is empty
                                    // otherwise we already know that it was a contract
                                    if (returndata.length == 0 && target.code.length == 0) {
                                        revert AddressEmptyCode(target);
                                    }
                                    return returndata;
                                }
                            }
                            /**
                             * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                             * revert reason or with a default {FailedInnerCall} error.
                             */
                            function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                                if (!success) {
                                    _revert(returndata);
                                } else {
                                    return returndata;
                                }
                            }
                            /**
                             * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
                             */
                            function _revert(bytes memory returndata) private pure {
                                // 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
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        let returndata_size := mload(returndata)
                                        revert(add(32, returndata), returndata_size)
                                    }
                                } else {
                                    revert FailedInnerCall();
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
                        pragma solidity ^0.8.20;
                        import {IERC721Receiver} from "../IERC721Receiver.sol";
                        /**
                         * @dev Implementation of the {IERC721Receiver} interface.
                         *
                         * Accepts all token transfers.
                         * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
                         * {IERC721-setApprovalForAll}.
                         */
                        abstract contract ERC721Holder is IERC721Receiver {
                            /**
                             * @dev See {IERC721Receiver-onERC721Received}.
                             *
                             * Always returns `IERC721Receiver.onERC721Received.selector`.
                             */
                            function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
                                return this.onERC721Received.selector;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/utils/ERC1155Holder.sol)
                        pragma solidity ^0.8.20;
                        import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
                        import {IERC1155Receiver} from "../IERC1155Receiver.sol";
                        /**
                         * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
                         *
                         * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
                         * stuck.
                         */
                        abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
                            /**
                             * @dev See {IERC165-supportsInterface}.
                             */
                            function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                                return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
                            }
                            function onERC1155Received(
                                address,
                                address,
                                uint256,
                                uint256,
                                bytes memory
                            ) public virtual override returns (bytes4) {
                                return this.onERC1155Received.selector;
                            }
                            function onERC1155BatchReceived(
                                address,
                                address,
                                uint256[] memory,
                                uint256[] memory,
                                bytes memory
                            ) public virtual override returns (bytes4) {
                                return this.onERC1155BatchReceived.selector;
                            }
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        /// @notice Arithmetic library with operations for fixed-point numbers.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
                        /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
                        library FixedPointMathLib {
                            /*//////////////////////////////////////////////////////////////
                                            SIMPLIFIED FIXED POINT OPERATIONS
                            //////////////////////////////////////////////////////////////*/
                            uint256 internal constant MAX_UINT256 = 2**256 - 1;
                            uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
                            function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
                                return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
                            }
                            function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
                                return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
                            }
                            function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
                                return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
                            }
                            function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
                                return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
                            }
                            /*//////////////////////////////////////////////////////////////
                                            LOW LEVEL FIXED POINT OPERATIONS
                            //////////////////////////////////////////////////////////////*/
                            function mulDivDown(
                                uint256 x,
                                uint256 y,
                                uint256 denominator
                            ) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                                    if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                                        revert(0, 0)
                                    }
                                    // Divide x * y by the denominator.
                                    z := div(mul(x, y), denominator)
                                }
                            }
                            function mulDivUp(
                                uint256 x,
                                uint256 y,
                                uint256 denominator
                            ) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                                    if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                                        revert(0, 0)
                                    }
                                    // If x * y modulo the denominator is strictly greater than 0,
                                    // 1 is added to round up the division of x * y by the denominator.
                                    z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
                                }
                            }
                            function rpow(
                                uint256 x,
                                uint256 n,
                                uint256 scalar
                            ) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    switch x
                                    case 0 {
                                        switch n
                                        case 0 {
                                            // 0 ** 0 = 1
                                            z := scalar
                                        }
                                        default {
                                            // 0 ** n = 0
                                            z := 0
                                        }
                                    }
                                    default {
                                        switch mod(n, 2)
                                        case 0 {
                                            // If n is even, store scalar in z for now.
                                            z := scalar
                                        }
                                        default {
                                            // If n is odd, store x in z for now.
                                            z := x
                                        }
                                        // Shifting right by 1 is like dividing by 2.
                                        let half := shr(1, scalar)
                                        for {
                                            // Shift n right by 1 before looping to halve it.
                                            n := shr(1, n)
                                        } n {
                                            // Shift n right by 1 each iteration to halve it.
                                            n := shr(1, n)
                                        } {
                                            // Revert immediately if x ** 2 would overflow.
                                            // Equivalent to iszero(eq(div(xx, x), x)) here.
                                            if shr(128, x) {
                                                revert(0, 0)
                                            }
                                            // Store x squared.
                                            let xx := mul(x, x)
                                            // Round to the nearest number.
                                            let xxRound := add(xx, half)
                                            // Revert if xx + half overflowed.
                                            if lt(xxRound, xx) {
                                                revert(0, 0)
                                            }
                                            // Set x to scaled xxRound.
                                            x := div(xxRound, scalar)
                                            // If n is even:
                                            if mod(n, 2) {
                                                // Compute z * x.
                                                let zx := mul(z, x)
                                                // If z * x overflowed:
                                                if iszero(eq(div(zx, x), z)) {
                                                    // Revert if x is non-zero.
                                                    if iszero(iszero(x)) {
                                                        revert(0, 0)
                                                    }
                                                }
                                                // Round to the nearest number.
                                                let zxRound := add(zx, half)
                                                // Revert if zx + half overflowed.
                                                if lt(zxRound, zx) {
                                                    revert(0, 0)
                                                }
                                                // Return properly scaled zxRound.
                                                z := div(zxRound, scalar)
                                            }
                                        }
                                    }
                                }
                            }
                            /*//////////////////////////////////////////////////////////////
                                                GENERAL NUMBER UTILITIES
                            //////////////////////////////////////////////////////////////*/
                            function sqrt(uint256 x) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    let y := x // We start y at x, which will help us make our initial estimate.
                                    z := 181 // The "correct" value is 1, but this saves a multiplication later.
                                    // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                                    // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
                                    // We check y >= 2^(k + 8) but shift right by k bits
                                    // each branch to ensure that if x >= 256, then y >= 256.
                                    if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                                        y := shr(128, y)
                                        z := shl(64, z)
                                    }
                                    if iszero(lt(y, 0x1000000000000000000)) {
                                        y := shr(64, y)
                                        z := shl(32, z)
                                    }
                                    if iszero(lt(y, 0x10000000000)) {
                                        y := shr(32, y)
                                        z := shl(16, z)
                                    }
                                    if iszero(lt(y, 0x1000000)) {
                                        y := shr(16, y)
                                        z := shl(8, z)
                                    }
                                    // Goal was to get z*z*y within a small factor of x. More iterations could
                                    // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
                                    // We ensured y >= 256 so that the relative difference between y and y+1 is small.
                                    // That's not possible if x < 256 but we can just verify those cases exhaustively.
                                    // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
                                    // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
                                    // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
                                    // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
                                    // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
                                    // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
                                    // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
                                    // There is no overflow risk here since y < 2^136 after the first branch above.
                                    z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
                                    // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    // If x+1 is a perfect square, the Babylonian method cycles between
                                    // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
                                    // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                                    // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                                    // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                                    z := sub(z, lt(div(x, z), z))
                                }
                            }
                            function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Mod x by y. Note this will return
                                    // 0 instead of reverting if y is zero.
                                    z := mod(x, y)
                                }
                            }
                            function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Divide x by y. Note this will return
                                    // 0 instead of reverting if y is zero.
                                    r := div(x, y)
                                }
                            }
                            function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Add 1 to x * y if x % y > 0. Note this will
                                    // return 0 instead of reverting if y is zero.
                                    z := add(gt(mod(x, y), 0), div(x, y))
                                }
                            }
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        import {ERC20} from "../tokens/ERC20.sol";
                        /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
                        /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
                        /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
                        library SafeTransferLib {
                            /*//////////////////////////////////////////////////////////////
                                                     ETH OPERATIONS
                            //////////////////////////////////////////////////////////////*/
                            function safeTransferETH(address to, uint256 amount) internal {
                                bool success;
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Transfer the ETH and store if it succeeded or not.
                                    success := call(gas(), to, amount, 0, 0, 0, 0)
                                }
                                require(success, "ETH_TRANSFER_FAILED");
                            }
                            /*//////////////////////////////////////////////////////////////
                                                    ERC20 OPERATIONS
                            //////////////////////////////////////////////////////////////*/
                            function safeTransferFrom(
                                ERC20 token,
                                address from,
                                address to,
                                uint256 amount
                            ) internal {
                                bool success;
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Get a pointer to some free memory.
                                    let freeMemoryPointer := mload(0x40)
                                    // Write the abi-encoded calldata into memory, beginning with the function selector.
                                    mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                                    mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
                                    mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                                    mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                                    success := and(
                                        // Set success to whether the call reverted, if not we check it either
                                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                                        or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                        // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                                        // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                        // Counterintuitively, this call must be positioned second to the or() call in the
                                        // surrounding and() call or else returndatasize() will be zero during the computation.
                                        call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                                    )
                                }
                                require(success, "TRANSFER_FROM_FAILED");
                            }
                            function safeTransfer(
                                ERC20 token,
                                address to,
                                uint256 amount
                            ) internal {
                                bool success;
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Get a pointer to some free memory.
                                    let freeMemoryPointer := mload(0x40)
                                    // Write the abi-encoded calldata into memory, beginning with the function selector.
                                    mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                                    mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                                    mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                                    success := and(
                                        // Set success to whether the call reverted, if not we check it either
                                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                                        or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                        // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                                        // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                        // Counterintuitively, this call must be positioned second to the or() call in the
                                        // surrounding and() call or else returndatasize() will be zero during the computation.
                                        call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                                    )
                                }
                                require(success, "TRANSFER_FAILED");
                            }
                            function safeApprove(
                                ERC20 token,
                                address to,
                                uint256 amount
                            ) internal {
                                bool success;
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Get a pointer to some free memory.
                                    let freeMemoryPointer := mload(0x40)
                                    // Write the abi-encoded calldata into memory, beginning with the function selector.
                                    mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                                    mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                                    mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                                    success := and(
                                        // Set success to whether the call reverted, if not we check it either
                                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                                        or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                        // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                                        // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                        // Counterintuitively, this call must be positioned second to the or() call in the
                                        // surrounding and() call or else returndatasize() will be zero during the computation.
                                        call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                                    )
                                }
                                require(success, "APPROVE_FAILED");
                            }
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
                        /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
                        /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
                        abstract contract ERC20 {
                            /*//////////////////////////////////////////////////////////////
                                                         EVENTS
                            //////////////////////////////////////////////////////////////*/
                            event Transfer(address indexed from, address indexed to, uint256 amount);
                            event Approval(address indexed owner, address indexed spender, uint256 amount);
                            /*//////////////////////////////////////////////////////////////
                                                    METADATA STORAGE
                            //////////////////////////////////////////////////////////////*/
                            string public name;
                            string public symbol;
                            uint8 public immutable decimals;
                            /*//////////////////////////////////////////////////////////////
                                                      ERC20 STORAGE
                            //////////////////////////////////////////////////////////////*/
                            uint256 public totalSupply;
                            mapping(address => uint256) public balanceOf;
                            mapping(address => mapping(address => uint256)) public allowance;
                            /*//////////////////////////////////////////////////////////////
                                                    EIP-2612 STORAGE
                            //////////////////////////////////////////////////////////////*/
                            uint256 internal immutable INITIAL_CHAIN_ID;
                            bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
                            mapping(address => uint256) public nonces;
                            /*//////////////////////////////////////////////////////////////
                                                       CONSTRUCTOR
                            //////////////////////////////////////////////////////////////*/
                            constructor(
                                string memory _name,
                                string memory _symbol,
                                uint8 _decimals
                            ) {
                                name = _name;
                                symbol = _symbol;
                                decimals = _decimals;
                                INITIAL_CHAIN_ID = block.chainid;
                                INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
                            }
                            /*//////////////////////////////////////////////////////////////
                                                       ERC20 LOGIC
                            //////////////////////////////////////////////////////////////*/
                            function approve(address spender, uint256 amount) public virtual returns (bool) {
                                allowance[msg.sender][spender] = amount;
                                emit Approval(msg.sender, spender, amount);
                                return true;
                            }
                            function transfer(address to, uint256 amount) public virtual returns (bool) {
                                balanceOf[msg.sender] -= amount;
                                // Cannot overflow because the sum of all user
                                // balances can't exceed the max uint256 value.
                                unchecked {
                                    balanceOf[to] += amount;
                                }
                                emit Transfer(msg.sender, to, amount);
                                return true;
                            }
                            function transferFrom(
                                address from,
                                address to,
                                uint256 amount
                            ) public virtual returns (bool) {
                                uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
                                if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
                                balanceOf[from] -= amount;
                                // Cannot overflow because the sum of all user
                                // balances can't exceed the max uint256 value.
                                unchecked {
                                    balanceOf[to] += amount;
                                }
                                emit Transfer(from, to, amount);
                                return true;
                            }
                            /*//////////////////////////////////////////////////////////////
                                                     EIP-2612 LOGIC
                            //////////////////////////////////////////////////////////////*/
                            function permit(
                                address owner,
                                address spender,
                                uint256 value,
                                uint256 deadline,
                                uint8 v,
                                bytes32 r,
                                bytes32 s
                            ) public virtual {
                                require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
                                // Unchecked because the only math done is incrementing
                                // the owner's nonce which cannot realistically overflow.
                                unchecked {
                                    address recoveredAddress = ecrecover(
                                        keccak256(
                                            abi.encodePacked(
                                                "\\x19\\x01",
                                                DOMAIN_SEPARATOR(),
                                                keccak256(
                                                    abi.encode(
                                                        keccak256(
                                                            "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                                        ),
                                                        owner,
                                                        spender,
                                                        value,
                                                        nonces[owner]++,
                                                        deadline
                                                    )
                                                )
                                            )
                                        ),
                                        v,
                                        r,
                                        s
                                    );
                                    require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                                    allowance[recoveredAddress][spender] = value;
                                }
                                emit Approval(owner, spender, value);
                            }
                            function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                                return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
                            }
                            function computeDomainSeparator() internal view virtual returns (bytes32) {
                                return
                                    keccak256(
                                        abi.encode(
                                            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                                            keccak256(bytes(name)),
                                            keccak256("1"),
                                            block.chainid,
                                            address(this)
                                        )
                                    );
                            }
                            /*//////////////////////////////////////////////////////////////
                                                INTERNAL MINT/BURN LOGIC
                            //////////////////////////////////////////////////////////////*/
                            function _mint(address to, uint256 amount) internal virtual {
                                totalSupply += amount;
                                // Cannot overflow because the sum of all user
                                // balances can't exceed the max uint256 value.
                                unchecked {
                                    balanceOf[to] += amount;
                                }
                                emit Transfer(address(0), to, amount);
                            }
                            function _burn(address from, uint256 amount) internal virtual {
                                balanceOf[from] -= amount;
                                // Cannot underflow because a user's balance
                                // will never be larger than the total supply.
                                unchecked {
                                    totalSupply -= amount;
                                }
                                emit Transfer(from, address(0), amount);
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.8.21;
                        interface BeforeTransferHook {
                            function beforeTransfer(address from, address to, address operator) external view;
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        /// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
                        /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
                        abstract contract Auth {
                            event OwnershipTransferred(address indexed user, address indexed newOwner);
                            event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
                            address public owner;
                            Authority public authority;
                            constructor(address _owner, Authority _authority) {
                                owner = _owner;
                                authority = _authority;
                                emit OwnershipTransferred(msg.sender, _owner);
                                emit AuthorityUpdated(msg.sender, _authority);
                            }
                            modifier requiresAuth() virtual {
                                require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
                                _;
                            }
                            function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
                                Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
                                // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
                                // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
                                return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
                            }
                            function setAuthority(Authority newAuthority) public virtual {
                                // We check if the caller is the owner first because we want to ensure they can
                                // always swap out the authority even if it's reverting or using up a lot of gas.
                                require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
                                authority = newAuthority;
                                emit AuthorityUpdated(msg.sender, newAuthority);
                            }
                            function transferOwnership(address newOwner) public virtual requiresAuth {
                                owner = newOwner;
                                emit OwnershipTransferred(msg.sender, newOwner);
                            }
                        }
                        /// @notice A generic interface for a contract which provides authorization data to an Auth instance.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
                        /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
                        interface Authority {
                            function canCall(
                                address user,
                                address target,
                                bytes4 functionSig
                            ) external view returns (bool);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
                        pragma solidity ^0.8.20;
                        /**
                         * @title ERC721 token receiver interface
                         * @dev Interface for any contract that wants to support safeTransfers
                         * from ERC721 asset contracts.
                         */
                        interface IERC721Receiver {
                            /**
                             * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                             * by `operator` from `from`, this function is called.
                             *
                             * It must return its Solidity selector to confirm the token transfer.
                             * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
                             * reverted.
                             *
                             * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
                             */
                            function onERC721Received(
                                address operator,
                                address from,
                                uint256 tokenId,
                                bytes calldata data
                            ) external returns (bytes4);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
                        pragma solidity ^0.8.20;
                        import {IERC165} from "./IERC165.sol";
                        /**
                         * @dev Implementation of the {IERC165} interface.
                         *
                         * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
                         * for the additional interface id that will be supported. For example:
                         *
                         * ```solidity
                         * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                         *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
                         * }
                         * ```
                         */
                        abstract contract ERC165 is IERC165 {
                            /**
                             * @dev See {IERC165-supportsInterface}.
                             */
                            function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                                return interfaceId == type(IERC165).interfaceId;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
                        pragma solidity ^0.8.20;
                        import {IERC165} from "../../utils/introspection/IERC165.sol";
                        /**
                         * @dev Interface that must be implemented by smart contracts in order to receive
                         * ERC-1155 token transfers.
                         */
                        interface IERC1155Receiver is IERC165 {
                            /**
                             * @dev Handles the receipt of a single ERC1155 token type. This function is
                             * called at the end of a `safeTransferFrom` after the balance has been updated.
                             *
                             * NOTE: To accept the transfer, this must return
                             * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                             * (i.e. 0xf23a6e61, or its own function selector).
                             *
                             * @param operator The address which initiated the transfer (i.e. msg.sender)
                             * @param from The address which previously owned the token
                             * @param id The ID of the token being transferred
                             * @param value The amount of tokens being transferred
                             * @param data Additional data with no specified format
                             * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
                             */
                            function onERC1155Received(
                                address operator,
                                address from,
                                uint256 id,
                                uint256 value,
                                bytes calldata data
                            ) external returns (bytes4);
                            /**
                             * @dev Handles the receipt of a multiple ERC1155 token types. This function
                             * is called at the end of a `safeBatchTransferFrom` after the balances have
                             * been updated.
                             *
                             * NOTE: To accept the transfer(s), this must return
                             * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                             * (i.e. 0xbc197c81, or its own function selector).
                             *
                             * @param operator The address which initiated the batch transfer (i.e. msg.sender)
                             * @param from The address which previously owned the token
                             * @param ids An array containing ids of each token being transferred (order and length must match values array)
                             * @param values An array containing amounts of each token being transferred (order and length must match ids array)
                             * @param data Additional data with no specified format
                             * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
                             */
                            function onERC1155BatchReceived(
                                address operator,
                                address from,
                                uint256[] calldata ids,
                                uint256[] calldata values,
                                bytes calldata data
                            ) external returns (bytes4);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
                        pragma solidity ^0.8.20;
                        /**
                         * @dev Interface of the ERC165 standard, as defined in the
                         * https://eips.ethereum.org/EIPS/eip-165[EIP].
                         *
                         * Implementers can declare support of contract interfaces, which can then be
                         * queried by others ({ERC165Checker}).
                         *
                         * For an implementation, see {ERC165}.
                         */
                        interface IERC165 {
                            /**
                             * @dev Returns true if this contract implements the interface defined by
                             * `interfaceId`. See the corresponding
                             * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                             * to learn more about how these ids are created.
                             *
                             * This function call must use less than 30 000 gas.
                             */
                            function supportsInterface(bytes4 interfaceId) external view returns (bool);
                        }
                        

                        File 5 of 15: Pool
                        // SPDX-License-Identifier: LGPL-3.0-or-later
                        pragma solidity 0.8.12;
                        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.12;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                          function _msgSender() internal view virtual returns (address payable) {
                            return 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.12;
                        /**
                         * @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: AGPL-3.0
                        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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import {IERC20} from './IERC20.sol';
                        interface IERC20Detailed is IERC20 {
                          function name() external view returns (string memory);
                          function symbol() external view returns (string memory);
                          function decimals() external view returns (uint8);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                        pragma solidity 0.8.12;
                        /**
                         * @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 '../../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: AGPL-3.0
                        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: 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 {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;
                        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: 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 {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;
                        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);
                          /**
                           * @dev Emitted when pool receives ETH from liquidation
                           * @param sender Address of the caller
                           * @param amount Amount of the ETH
                           * @param permissionKey permission key that was used for the auction
                           */
                          event PoolReceivedETH(address sender, uint256 amount, bytes permissionKey);
                          /**
                           * @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://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 -> 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://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 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 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 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 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: AGPL-3.0
                        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: AGPL-3.0
                        pragma solidity ^0.8.0;
                        import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                        /**
                         * @title IReserveInterestRateStrategy
                         * @author Aave
                         * @notice Interface for the calculation of the interest rates
                         */
                        interface IReserveInterestRateStrategy {
                          /**
                           * @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 rays
                           * @return stableBorrowRate The stable borrow rate expressed in rays
                           * @return variableBorrowRate The variable borrow rate expressed in rays
                           */
                          function calculateInterestRates(
                            DataTypes.CalculateInterestRatesParams memory params
                          ) external view returns (uint256, uint256, uint256);
                        }
                        // 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 {IInitializableDebtToken} from './IInitializableDebtToken.sol';
                        /**
                         * @title IStableDebtToken
                         * @author Aave
                         * @notice Defines the interface for the stable debt token
                         * @dev It does not inherit from IERC20 to save in code size
                         */
                        interface IStableDebtToken is IInitializableDebtToken {
                          /**
                           * @dev Emitted when new stable debt is minted
                           * @param user The address of the user who triggered the minting
                           * @param onBehalfOf The recipient of stable debt tokens
                           * @param amount The amount minted (user entered amount + balance increase from interest)
                           * @param currentBalance The balance of the user based on the previous balance and balance increase from interest
                           * @param balanceIncrease The increase in balance since the last action of the user 'onBehalfOf'
                           * @param newRate The rate of the debt after the minting
                           * @param avgStableRate The next average stable rate after the minting
                           * @param newTotalSupply The next total supply of the stable debt token after the action
                           */
                          event Mint(
                            address indexed user,
                            address indexed onBehalfOf,
                            uint256 amount,
                            uint256 currentBalance,
                            uint256 balanceIncrease,
                            uint256 newRate,
                            uint256 avgStableRate,
                            uint256 newTotalSupply
                          );
                          /**
                           * @dev Emitted when new stable debt is burned
                           * @param from The address from which the debt will be burned
                           * @param amount The amount being burned (user entered amount - balance increase from interest)
                           * @param currentBalance The balance of the user based on the previous balance and balance increase from interest
                           * @param balanceIncrease The increase in balance since the last action of 'from'
                           * @param avgStableRate The next average stable rate after the burning
                           * @param newTotalSupply The next total supply of the stable debt token after the action
                           */
                          event Burn(
                            address indexed from,
                            uint256 amount,
                            uint256 currentBalance,
                            uint256 balanceIncrease,
                            uint256 avgStableRate,
                            uint256 newTotalSupply
                          );
                          /**
                           * @notice Mints debt token to the `onBehalfOf` address.
                           * @dev The resulting rate is the weighted average between the rate of the new debt
                           * and the rate of the previous debt
                           * @param user The address receiving the borrowed underlying, being the delegatee in case
                           * of credit delegate, or same as `onBehalfOf` otherwise
                           * @param onBehalfOf The address receiving the debt tokens
                           * @param amount The amount of debt tokens to mint
                           * @param rate The rate of the debt being minted
                           * @return True if it is the first borrow, false otherwise
                           * @return The total stable debt
                           * @return The average stable borrow rate
                           */
                          function mint(
                            address user,
                            address onBehalfOf,
                            uint256 amount,
                            uint256 rate
                          ) external returns (bool, uint256, uint256);
                          /**
                           * @notice Burns debt of `user`
                           * @dev The resulting rate is the weighted average between the rate of the new debt
                           * and the rate of the previous debt
                           * @dev In some instances, a burn transaction will emit a mint event
                           * if the amount to burn is less than the interest the user earned
                           * @param from The address from which the debt will be burned
                           * @param amount The amount of debt tokens getting burned
                           * @return The total stable debt
                           * @return The average stable borrow rate
                           */
                          function burn(address from, uint256 amount) external returns (uint256, uint256);
                          /**
                           * @notice Returns the average rate of all the stable rate loans.
                           * @return The average stable rate
                           */
                          function getAverageStableRate() external view returns (uint256);
                          /**
                           * @notice Returns the stable rate of the user debt
                           * @param user The address of the user
                           * @return The stable rate of the user
                           */
                          function getUserStableRate(address user) external view returns (uint256);
                          /**
                           * @notice Returns the timestamp of the last update of the user
                           * @param user The address of the user
                           * @return The timestamp
                           */
                          function getUserLastUpdated(address user) external view returns (uint40);
                          /**
                           * @notice Returns the principal, the total supply, the average stable rate and the timestamp for the last update
                           * @return The principal
                           * @return The total supply
                           * @return The average stable rate
                           * @return The timestamp of the last update
                           */
                          function getSupplyData() external view returns (uint256, uint256, uint256, uint40);
                          /**
                           * @notice Returns the timestamp of the last update of the total supply
                           * @return The timestamp
                           */
                          function getTotalSupplyLastUpdated() external view returns (uint40);
                          /**
                           * @notice Returns the total supply and the average stable rate
                           * @return The total supply
                           * @return The average rate
                           */
                          function getTotalSupplyAndAvgRate() external view returns (uint256, uint256);
                          /**
                           * @notice Returns the principal debt balance of the user
                           * @return The debt balance of the user since the last burn/mint action
                           */
                          function principalBalanceOf(address user) external view returns (uint256);
                          /**
                           * @notice Returns the address of the underlying asset of this stableDebtToken (E.g. WETH for stableDebtWETH)
                           * @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.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.12;
                        /**
                         * @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;
                        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 STABLE_BORROWING_MASK =          0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // 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
                          uint256 internal constant EMODE_CATEGORY_MASK =            0xFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                          uint256 internal constant UNBACKED_MINT_CAP_MASK =         0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                          uint256 internal constant DEBT_CEILING_MASK =              0xF0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 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 STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59;
                          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;
                          uint256 internal constant EMODE_CATEGORY_START_BIT_POSITION = 168;
                          uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176;
                          uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212;
                          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_EMODE_CATEGORY = 255;
                          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 Enables or disables stable rate borrowing on the reserve
                           * @param self The reserve configuration
                           * @param enabled True if the stable rate borrowing needs to be enabled, false otherwise
                           */
                          function setStableRateBorrowingEnabled(
                            DataTypes.ReserveConfigurationMap memory self,
                            bool enabled
                          ) internal pure {
                            self.data =
                              (self.data & STABLE_BORROWING_MASK) |
                              (uint256(enabled ? 1 : 0) << STABLE_BORROWING_ENABLED_START_BIT_POSITION);
                          }
                          /**
                           * @notice Gets the stable rate borrowing state of the reserve
                           * @param self The reserve configuration
                           * @return The stable rate borrowing state
                           */
                          function getStableRateBorrowingEnabled(
                            DataTypes.ReserveConfigurationMap memory self
                          ) internal pure returns (bool) {
                            return (self.data & ~STABLE_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 eMode asset category
                           * @param self The reserve configuration
                           * @param category The asset category when the user selects the eMode
                           */
                          function setEModeCategory(
                            DataTypes.ReserveConfigurationMap memory self,
                            uint256 category
                          ) internal pure {
                            require(category <= MAX_VALID_EMODE_CATEGORY, Errors.INVALID_EMODE_CATEGORY);
                            self.data = (self.data & EMODE_CATEGORY_MASK) | (category << EMODE_CATEGORY_START_BIT_POSITION);
                          }
                          /**
                           * @dev Gets the eMode asset category
                           * @param self The reserve configuration
                           * @return The eMode category for the asset
                           */
                          function getEModeCategory(
                            DataTypes.ReserveConfigurationMap memory self
                          ) internal pure returns (uint256) {
                            return (self.data & ~EMODE_CATEGORY_MASK) >> EMODE_CATEGORY_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 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 stableRateBorrowing enabled
                           * @return The state flag representing paused
                           */
                          function getFlags(
                            DataTypes.ReserveConfigurationMap memory self
                          ) internal pure returns (bool, bool, bool, bool, bool) {
                            uint256 dataLocal = self.data;
                            return (
                              (dataLocal & ~ACTIVE_MASK) != 0,
                              (dataLocal & ~FROZEN_MASK) != 0,
                              (dataLocal & ~BORROWING_MASK) != 0,
                              (dataLocal & ~STABLE_BORROWING_MASK) != 0,
                              (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
                           * @return The state param representing eMode category
                           */
                          function getParams(
                            DataTypes.ReserveConfigurationMap memory self
                          ) internal pure returns (uint256, 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,
                              (dataLocal & ~EMODE_CATEGORY_MASK) >> EMODE_CATEGORY_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.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: 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_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 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;
                        import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {DataTypes} from '../types/DataTypes.sol';
                        /**
                         * @title Helpers library
                         * @author Aave
                         */
                        library Helpers {
                          /**
                           * @notice Fetches the user current stable and variable debt balances
                           * @param user The user address
                           * @param reserveCache The reserve cache data object
                           * @return The stable debt balance
                           * @return The variable debt balance
                           */
                          function getUserCurrentDebt(
                            address user,
                            DataTypes.ReserveCache memory reserveCache
                          ) internal view returns (uint256, uint256) {
                            return (
                              IERC20(reserveCache.stableDebtTokenAddress).balanceOf(user),
                              IERC20(reserveCache.variableDebtTokenAddress).balanceOf(user)
                            );
                          }
                        }
                        // SPDX-License-Identifier: MIT
                        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 {IStableDebtToken} from '../../../interfaces/IStableDebtToken.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 {Helpers} from '../helpers/Helpers.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 RebalanceStableBorrowRate(address indexed reserve, address indexed user);
                          event SwapBorrowRateMode(
                            address indexed reserve,
                            address indexed user,
                            DataTypes.InterestRateMode interestRateMode
                          );
                          event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
                          /**
                           * @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
                          ) public {
                            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,
                                maxStableLoanPercent: params.maxStableRateBorrowSizePercent,
                                reservesCount: params.reservesCount,
                                oracle: params.oracle,
                                userEModeCategory: params.userEModeCategory,
                                priceOracleSentinel: params.priceOracleSentinel,
                                isolationModeActive: isolationModeActive,
                                isolationModeCollateralAddress: isolationModeCollateralAddress,
                                isolationModeDebtCeiling: isolationModeDebtCeiling
                              })
                            );
                            uint256 currentStableRate = 0;
                            bool isFirstBorrowing = false;
                            if (params.interestRateMode == DataTypes.InterestRateMode.STABLE) {
                              currentStableRate = reserve.currentStableBorrowRate;
                              (
                                isFirstBorrowing,
                                reserveCache.nextTotalStableDebt,
                                reserveCache.nextAvgStableBorrowRate
                              ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).mint(
                                params.user,
                                params.onBehalfOf,
                                params.amount,
                                currentStableRate
                              );
                            } else {
                              (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.updateInterestRates(
                              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,
                              params.interestRateMode,
                              params.interestRateMode == DataTypes.InterestRateMode.STABLE
                                ? currentStableRate
                                : 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 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(
                              params.onBehalfOf,
                              reserveCache
                            );
                            ValidationLogic.validateRepay(
                              reserveCache,
                              params.amount,
                              params.interestRateMode,
                              params.onBehalfOf,
                              stableDebt,
                              variableDebt
                            );
                            uint256 paybackAmount = params.interestRateMode == DataTypes.InterestRateMode.STABLE
                              ? stableDebt
                              : 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;
                            }
                            if (params.interestRateMode == DataTypes.InterestRateMode.STABLE) {
                              (reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate) = IStableDebtToken(
                                reserveCache.stableDebtTokenAddress
                              ).burn(params.onBehalfOf, paybackAmount);
                            } else {
                              reserveCache.nextScaledVariableDebt = IVariableDebtToken(
                                reserveCache.variableDebtTokenAddress
                              ).burn(params.onBehalfOf, paybackAmount, reserveCache.nextVariableBorrowIndex);
                            }
                            reserve.updateInterestRates(
                              reserveCache,
                              params.asset,
                              params.useATokens ? 0 : paybackAmount,
                              0
                            );
                            if (stableDebt + 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
                              );
                            } 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;
                          }
                          /**
                           * @notice Implements the rebalance stable borrow rate feature. In case of liquidity crunches on the protocol, stable
                           * rate borrows might need to be rebalanced to bring back equilibrium between the borrow and supply APYs.
                           * @dev The rules that define if a position can be rebalanced are implemented in `ValidationLogic.validateRebalanceStableBorrowRate()`
                           * @dev Emits the `RebalanceStableBorrowRate()` event
                           * @param reserve The state of the reserve of the asset being repaid
                           * @param asset The asset of the position being rebalanced
                           * @param user The user being rebalanced
                           */
                          function executeRebalanceStableBorrowRate(
                            DataTypes.ReserveData storage reserve,
                            address asset,
                            address user
                          ) external {
                            DataTypes.ReserveCache memory reserveCache = reserve.cache();
                            reserve.updateState(reserveCache);
                            ValidationLogic.validateRebalanceStableBorrowRate(reserve, reserveCache, asset);
                            IStableDebtToken stableDebtToken = IStableDebtToken(reserveCache.stableDebtTokenAddress);
                            uint256 stableDebt = IERC20(address(stableDebtToken)).balanceOf(user);
                            stableDebtToken.burn(user, stableDebt);
                            (, reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate) = stableDebtToken
                              .mint(user, user, stableDebt, reserve.currentStableBorrowRate);
                            reserve.updateInterestRates(reserveCache, asset, 0, 0);
                            emit RebalanceStableBorrowRate(asset, user);
                          }
                          /**
                           * @notice Implements the swap borrow rate feature. Borrowers can swap from variable to stable positions at any time.
                           * @dev Emits the `Swap()` event
                           * @param reserve The of the reserve of the asset being repaid
                           * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
                           * @param asset The asset of the position being swapped
                           * @param interestRateMode The current interest rate mode of the position being swapped
                           */
                          function executeSwapBorrowRateMode(
                            DataTypes.ReserveData storage reserve,
                            DataTypes.UserConfigurationMap storage userConfig,
                            address asset,
                            DataTypes.InterestRateMode interestRateMode
                          ) external {
                            DataTypes.ReserveCache memory reserveCache = reserve.cache();
                            reserve.updateState(reserveCache);
                            (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(
                              msg.sender,
                              reserveCache
                            );
                            ValidationLogic.validateSwapRateMode(
                              reserve,
                              reserveCache,
                              userConfig,
                              stableDebt,
                              variableDebt,
                              interestRateMode
                            );
                            if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
                              (reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate) = IStableDebtToken(
                                reserveCache.stableDebtTokenAddress
                              ).burn(msg.sender, stableDebt);
                              (, reserveCache.nextScaledVariableDebt) = IVariableDebtToken(
                                reserveCache.variableDebtTokenAddress
                              ).mint(msg.sender, msg.sender, stableDebt, reserveCache.nextVariableBorrowIndex);
                            } else {
                              reserveCache.nextScaledVariableDebt = IVariableDebtToken(
                                reserveCache.variableDebtTokenAddress
                              ).burn(msg.sender, variableDebt, reserveCache.nextVariableBorrowIndex);
                              (, reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate) = IStableDebtToken(
                                reserveCache.stableDebtTokenAddress
                              ).mint(msg.sender, msg.sender, variableDebt, reserve.currentStableBorrowRate);
                            }
                            reserve.updateInterestRates(reserveCache, asset, 0, 0);
                            emit SwapBorrowRateMode(asset, msg.sender, interestRateMode);
                          }
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        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);
                            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.updateInterestRates(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.updateInterestRates(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.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 {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 {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 {
                            ValidationLogic.validateSetUserEMode(
                              reservesData,
                              reservesList,
                              eModeCategories,
                              userConfig,
                              params.reservesCount,
                              params.categoryId
                            );
                            uint8 prevCategoryId = usersEModeCategory[msg.sender];
                            usersEModeCategory[msg.sender] = params.categoryId;
                            if (prevCategoryId != 0) {
                              ValidationLogic.validateHealthFactor(
                                reservesData,
                                reservesList,
                                eModeCategories,
                                userConfig,
                                msg.sender,
                                params.categoryId,
                                params.reservesCount,
                                params.oracle
                              );
                            }
                            emit UserEModeSet(msg.sender, params.categoryId);
                          }
                          /**
                           * @notice Gets the eMode configuration and calculates the eMode asset price if a custom oracle is configured
                           * @dev The eMode asset price returned is 0 if no oracle is specified
                           * @param category The user eMode category
                           * @param oracle The price oracle
                           * @return The eMode ltv
                           * @return The eMode liquidation threshold
                           * @return The eMode asset price
                           */
                          function getEModeConfiguration(
                            DataTypes.EModeCategory storage category,
                            IPriceOracleGetter oracle
                          ) internal view returns (uint256, uint256, uint256) {
                            uint256 eModeAssetPrice = 0;
                            address eModePriceSource = category.priceSource;
                            if (eModePriceSource != address(0)) {
                              eModeAssetPrice = oracle.getAssetPrice(eModePriceSource);
                            }
                            return (category.ltv, category.liquidationThreshold, eModeAssetPrice);
                          }
                          /**
                           * @notice Checks if eMode is active for a user and if yes, if the asset belongs to the eMode category chosen
                           * @param eModeUserCategory The user eMode category
                           * @param eModeAssetCategory The asset eMode category
                           * @return True if eMode is active and the asset belongs to the eMode category chosen by the user, false otherwise
                           */
                          function isInEModeCategory(
                            uint256 eModeUserCategory,
                            uint256 eModeAssetCategory
                          ) internal pure returns (bool) {
                            return (eModeUserCategory != 0 && eModeAssetCategory == eModeUserCategory);
                          }
                        }
                        // SPDX-License-Identifier: MIT
                        pragma solidity ^0.8.12;
                        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 '../../../flashloan/interfaces/IFlashLoanReceiver.sol';
                        import {IFlashLoanSimpleReceiver} from '../../../flashloan/interfaces/IFlashLoanSimpleReceiver.sol';
                        import {IPoolAddressesProvider} from '../../../interfaces/IPoolAddressesProvider.sol';
                        import {UserConfiguration} from '../configuration/UserConfiguration.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;
                            uint256 i;
                            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 (vars.i = 0; vars.i < params.assets.length; vars.i++) {
                              vars.currentAmount = params.amounts[vars.i];
                              vars.totalPremiums[vars.i] = DataTypes.InterestRateMode(params.interestRateModes[vars.i]) ==
                                DataTypes.InterestRateMode.NONE
                                ? vars.currentAmount.percentMul(vars.flashloanPremiumTotal)
                                : 0;
                              IAToken(reservesData[params.assets[vars.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 (vars.i = 0; vars.i < params.assets.length; vars.i++) {
                              vars.currentAsset = params.assets[vars.i];
                              vars.currentAmount = params.amounts[vars.i];
                              if (
                                DataTypes.InterestRateMode(params.interestRateModes[vars.i]) ==
                                DataTypes.InterestRateMode.NONE
                              ) {
                                _handleFlashLoanRepayment(
                                  reservesData[vars.currentAsset],
                                  DataTypes.FlashLoanRepaymentParams({
                                    asset: vars.currentAsset,
                                    receiverAddress: params.receiverAddress,
                                    amount: vars.currentAmount,
                                    totalPremium: vars.totalPremiums[vars.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[vars.i]),
                                    referralCode: params.referralCode,
                                    releaseUnderlying: false,
                                    maxStableRateBorrowSizePercent: IPool(params.pool).MAX_STABLE_RATE_BORROW_SIZE_PERCENT(),
                                    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[vars.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);
                            IFlashLoanSimpleReceiver receiver = IFlashLoanSimpleReceiver(params.receiverAddress);
                            uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal);
                            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.updateInterestRates(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(0),
                              params.totalPremium,
                              params.referralCode
                            );
                          }
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        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 {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 eModeAssetPrice;
                            uint256 eModeLtv;
                            uint256 eModeLiqThreshold;
                            uint256 eModeAssetCategory;
                            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, vars.eModeLiqThreshold, vars.eModeAssetPrice) = EModeLogic
                                .getEModeConfiguration(
                                  eModeCategories[params.userEModeCategory],
                                  IPriceOracleGetter(params.oracle)
                                );
                            }
                            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,
                                ,
                                vars.eModeAssetCategory
                              ) = currentReserve.configuration.getParams();
                              unchecked {
                                vars.assetUnit = 10 ** vars.decimals;
                              }
                              vars.assetPrice = vars.eModeAssetPrice != 0 &&
                                params.userEModeCategory == vars.eModeAssetCategory
                                ? vars.eModeAssetPrice
                                : 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 = EModeLogic.isInEModeCategory(
                                  params.userEModeCategory,
                                  vars.eModeAssetCategory
                                );
                                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 stable and variable debt tokens 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) {
                              userTotalDebt = userTotalDebt.rayMul(reserve.getNormalizedDebt());
                            }
                            userTotalDebt = userTotalDebt + IERC20(reserve.stableDebtTokenAddress).balanceOf(user);
                            userTotalDebt = assetPrice * userTotalDebt;
                            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: BUSL-1.1
                        pragma solidity 0.8.12;
                        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: BUSL-1.1
                        pragma solidity 0.8.12;
                        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 {Helpers} from '../../libraries/helpers/Helpers.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 {IAToken} from '../../../interfaces/IAToken.sol';
                        import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.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 userVariableDebt;
                            uint256 userTotalDebt;
                            uint256 actualDebtToLiquidate;
                            uint256 actualCollateralToLiquidate;
                            uint256 liquidationBonus;
                            uint256 healthFactor;
                            uint256 liquidationProtocolFeeAmount;
                            address collateralPriceSource;
                            address debtPriceSource;
                            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.userVariableDebt, vars.userTotalDebt, vars.actualDebtToLiquidate) = _calculateDebt(
                              vars.debtReserveCache,
                              params,
                              vars.healthFactor
                            );
                            ValidationLogic.validateLiquidationCall(
                              userConfig,
                              collateralReserve,
                              DataTypes.ValidateLiquidationCallParams({
                                debtReserveCache: vars.debtReserveCache,
                                totalDebt: vars.userTotalDebt,
                                healthFactor: vars.healthFactor,
                                priceOracleSentinel: params.priceOracleSentinel
                              })
                            );
                            (
                              vars.collateralAToken,
                              vars.collateralPriceSource,
                              vars.debtPriceSource,
                              vars.liquidationBonus
                            ) = _getConfigurationData(eModeCategories, collateralReserve, params);
                            vars.userCollateralBalance = vars.collateralAToken.balanceOf(params.user);
                            (
                              vars.actualCollateralToLiquidate,
                              vars.actualDebtToLiquidate,
                              vars.liquidationProtocolFeeAmount
                            ) = _calculateAvailableCollateralToLiquidate(
                              collateralReserve,
                              vars.debtReserveCache,
                              vars.collateralPriceSource,
                              vars.debtPriceSource,
                              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.updateInterestRates(
                              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.updateInterestRates(
                              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 {
                            if (vars.userVariableDebt >= vars.actualDebtToLiquidate) {
                              vars.debtReserveCache.nextScaledVariableDebt = IVariableDebtToken(
                                vars.debtReserveCache.variableDebtTokenAddress
                              ).burn(
                                  params.user,
                                  vars.actualDebtToLiquidate,
                                  vars.debtReserveCache.nextVariableBorrowIndex
                                );
                            } else {
                              // If the user doesn't have variable debt, no need to try to burn variable debt tokens
                              if (vars.userVariableDebt != 0) {
                                vars.debtReserveCache.nextScaledVariableDebt = IVariableDebtToken(
                                  vars.debtReserveCache.variableDebtTokenAddress
                                ).burn(params.user, vars.userVariableDebt, vars.debtReserveCache.nextVariableBorrowIndex);
                              }
                              (
                                vars.debtReserveCache.nextTotalStableDebt,
                                vars.debtReserveCache.nextAvgStableBorrowRate
                              ) = IStableDebtToken(vars.debtReserveCache.stableDebtTokenAddress).burn(
                                params.user,
                                vars.actualDebtToLiquidate - vars.userVariableDebt
                              );
                            }
                          }
                          /**
                           * @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 variable debt of the user
                           * @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) {
                            (uint256 userStableDebt, uint256 userVariableDebt) = Helpers.getUserCurrentDebt(
                              params.user,
                              debtReserveCache
                            );
                            uint256 userTotalDebt = userStableDebt + userVariableDebt;
                            uint256 closeFactor = healthFactor > CLOSE_FACTOR_HF_THRESHOLD
                              ? DEFAULT_LIQUIDATION_CLOSE_FACTOR
                              : MAX_LIQUIDATION_CLOSE_FACTOR;
                            uint256 maxLiquidatableDebt = userTotalDebt.percentMul(closeFactor);
                            uint256 actualDebtToLiquidate = params.debtToCover > maxLiquidatableDebt
                              ? maxLiquidatableDebt
                              : params.debtToCover;
                            return (userVariableDebt, userTotalDebt, actualDebtToLiquidate);
                          }
                          /**
                           * @notice Returns the configuration data for the debt and the collateral reserves.
                           * @param eModeCategories The configuration of all the efficiency mode categories
                           * @param collateralReserve The data of the collateral reserve
                           * @param params The additional parameters needed to execute the liquidation function
                           * @return The collateral aToken
                           * @return The address to use as price source for the collateral
                           * @return The address to use as price source for the debt
                           * @return The liquidation bonus to apply to the collateral
                           */
                          function _getConfigurationData(
                            mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                            DataTypes.ReserveData storage collateralReserve,
                            DataTypes.ExecuteLiquidationCallParams memory params
                          ) internal view returns (IAToken, address, address, uint256) {
                            IAToken collateralAToken = IAToken(collateralReserve.aTokenAddress);
                            uint256 liquidationBonus = collateralReserve.configuration.getLiquidationBonus();
                            address collateralPriceSource = params.collateralAsset;
                            address debtPriceSource = params.debtAsset;
                            if (params.userEModeCategory != 0) {
                              address eModePriceSource = eModeCategories[params.userEModeCategory].priceSource;
                              if (
                                EModeLogic.isInEModeCategory(
                                  params.userEModeCategory,
                                  collateralReserve.configuration.getEModeCategory()
                                )
                              ) {
                                liquidationBonus = eModeCategories[params.userEModeCategory].liquidationBonus;
                                if (eModePriceSource != address(0)) {
                                  collateralPriceSource = eModePriceSource;
                                }
                              }
                              // when in eMode, debt will always be in the same eMode category, can skip matching category check
                              if (eModePriceSource != address(0)) {
                                debtPriceSource = eModePriceSource;
                              }
                            }
                            return (collateralAToken, collateralPriceSource, debtPriceSource, liquidationBonus);
                          }
                          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: BUSL-1.1
                        pragma solidity 0.8.12;
                        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.stableDebtAddress,
                              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 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.12;
                        import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                        import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol';
                        import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
                        import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
                        import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                        import {MathUtils} from '../math/MathUtils.sol';
                        import {WadRayMath} from '../math/WadRayMath.sol';
                        import {PercentageMath} from '../math/PercentageMath.sol';
                        import {Errors} from '../helpers/Errors.sol';
                        import {DataTypes} from '../types/DataTypes.sol';
                        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 stableDebtTokenAddress The address of the overlying stable debt token 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 stableDebtTokenAddress,
                            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.stableDebtTokenAddress = stableDebtTokenAddress;
                            reserve.variableDebtTokenAddress = variableDebtTokenAddress;
                            reserve.interestRateStrategyAddress = interestRateStrategyAddress;
                          }
                          struct UpdateInterestRatesLocalVars {
                            uint256 nextLiquidityRate;
                            uint256 nextStableRate;
                            uint256 nextVariableRate;
                            uint256 totalVariableDebt;
                          }
                          /**
                           * @notice Updates the reserve current stable borrow rate, the 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 updateInterestRates(
                            DataTypes.ReserveData storage reserve,
                            DataTypes.ReserveCache memory reserveCache,
                            address reserveAddress,
                            uint256 liquidityAdded,
                            uint256 liquidityTaken
                          ) internal {
                            UpdateInterestRatesLocalVars memory vars;
                            vars.totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul(
                              reserveCache.nextVariableBorrowIndex
                            );
                            (
                              vars.nextLiquidityRate,
                              vars.nextStableRate,
                              vars.nextVariableRate
                            ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates(
                              DataTypes.CalculateInterestRatesParams({
                                unbacked: reserve.unbacked,
                                liquidityAdded: liquidityAdded,
                                liquidityTaken: liquidityTaken,
                                totalStableDebt: reserveCache.nextTotalStableDebt,
                                totalVariableDebt: vars.totalVariableDebt,
                                averageStableBorrowRate: reserveCache.nextAvgStableBorrowRate,
                                reserveFactor: reserveCache.reserveFactor,
                                reserve: reserveAddress,
                                aToken: reserveCache.aTokenAddress
                              })
                            );
                            reserve.currentLiquidityRate = vars.nextLiquidityRate.toUint128();
                            reserve.currentStableBorrowRate = vars.nextStableRate.toUint128();
                            reserve.currentVariableBorrowRate = vars.nextVariableRate.toUint128();
                            emit ReserveDataUpdated(
                              reserveAddress,
                              vars.nextLiquidityRate,
                              vars.nextStableRate,
                              vars.nextVariableRate,
                              reserveCache.nextLiquidityIndex,
                              reserveCache.nextVariableBorrowIndex
                            );
                          }
                          struct AccrueToTreasuryLocalVars {
                            uint256 prevTotalStableDebt;
                            uint256 prevTotalVariableDebt;
                            uint256 currTotalVariableDebt;
                            uint256 cumulatedStableInterest;
                            uint256 totalDebtAccrued;
                            uint256 amountToMint;
                          }
                          /**
                           * @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 {
                            AccrueToTreasuryLocalVars memory vars;
                            if (reserveCache.reserveFactor == 0) {
                              return;
                            }
                            //calculate the total variable debt at moment of the last interaction
                            vars.prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                              reserveCache.currVariableBorrowIndex
                            );
                            //calculate the new total variable debt after accumulation of the interest on the index
                            vars.currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                              reserveCache.nextVariableBorrowIndex
                            );
                            //calculate the stable debt until the last timestamp update
                            vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest(
                              reserveCache.currAvgStableBorrowRate,
                              reserveCache.stableDebtLastUpdateTimestamp,
                              reserveCache.reserveLastUpdateTimestamp
                            );
                            vars.prevTotalStableDebt = reserveCache.currPrincipalStableDebt.rayMul(
                              vars.cumulatedStableInterest
                            );
                            //debt accrued is the sum of the current debt minus the sum of the debt at the last update
                            vars.totalDebtAccrued =
                              vars.currTotalVariableDebt +
                              reserveCache.currTotalStableDebt -
                              vars.prevTotalVariableDebt -
                              vars.prevTotalStableDebt;
                            vars.amountToMint = vars.totalDebtAccrued.percentMul(reserveCache.reserveFactor);
                            if (vars.amountToMint != 0) {
                              reserve.accruedToTreasury += vars
                                .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.stableDebtTokenAddress = reserve.stableDebtTokenAddress;
                            reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
                            reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp;
                            reserveCache.currScaledVariableDebt = reserveCache.nextScaledVariableDebt = IVariableDebtToken(
                              reserveCache.variableDebtTokenAddress
                            ).scaledTotalSupply();
                            (
                              reserveCache.currPrincipalStableDebt,
                              reserveCache.currTotalStableDebt,
                              reserveCache.currAvgStableBorrowRate,
                              reserveCache.stableDebtLastUpdateTimestamp
                            ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData();
                            // by default the actions are considered as not affecting the debt balances.
                            // if the action involves mint/burn of debt, the cache needs to be updated
                            reserveCache.nextTotalStableDebt = reserveCache.currTotalStableDebt;
                            reserveCache.nextAvgStableBorrowRate = reserveCache.currAvgStableBorrowRate;
                            return reserveCache;
                          }
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        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);
                            reserve.updateInterestRates(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();
                            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.updateInterestRates(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;
                            if (params.from != params.to && params.amount != 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.12;
                        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 {IStableDebtToken} from '../../../interfaces/IStableDebtToken.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 {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
                          ) 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);
                            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 eModePriceSource;
                            address siloedBorrowingAddress;
                            bool isActive;
                            bool isFrozen;
                            bool isPaused;
                            bool borrowingEnabled;
                            bool stableRateBorrowingEnabled;
                            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.stableRateBorrowingEnabled,
                              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.priceOracleSentinel == address(0) ||
                                IPriceOracleSentinel(params.priceOracleSentinel).isBorrowAllowed(),
                              Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                            );
                            //validate interest rate mode
                            require(
                              params.interestRateMode == DataTypes.InterestRateMode.VARIABLE ||
                                params.interestRateMode == DataTypes.InterestRateMode.STABLE,
                              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 =
                                params.reserveCache.currTotalStableDebt +
                                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(
                                params.reserveCache.reserveConfiguration.getEModeCategory() == params.userEModeCategory,
                                Errors.INCONSISTENT_EMODE_CATEGORY
                              );
                              vars.eModePriceSource = eModeCategories[params.userEModeCategory].priceSource;
                            }
                            (
                              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(
                                vars.eModePriceSource != address(0) ? vars.eModePriceSource : 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
                            );
                            /**
                             * Following conditions need to be met if the user is borrowing at a stable rate:
                             * 1. Reserve must be enabled for stable rate borrowing
                             * 2. Users cannot borrow from the reserve if their collateral is (mostly) the same currency
                             *    they are borrowing, to prevent abuses.
                             * 3. Users will be able to borrow only a portion of the total available liquidity
                             */
                            if (params.interestRateMode == DataTypes.InterestRateMode.STABLE) {
                              //check if the borrow mode is stable and if stable rate borrowing is enabled on this reserve
                              require(vars.stableRateBorrowingEnabled, Errors.STABLE_BORROWING_NOT_ENABLED);
                              require(
                                !params.userConfig.isUsingAsCollateral(reservesData[params.asset].id) ||
                                  params.reserveCache.reserveConfiguration.getLtv() == 0 ||
                                  params.amount > IERC20(params.reserveCache.aTokenAddress).balanceOf(params.userAddress),
                                Errors.COLLATERAL_SAME_AS_BORROWING_CURRENCY
                              );
                              vars.availableLiquidity = IERC20(params.asset).balanceOf(params.reserveCache.aTokenAddress);
                              //calculate the max available loan size in stable rate mode as a percentage of the
                              //available liquidity
                              uint256 maxLoanSizeStable = vars.availableLiquidity.percentMul(params.maxStableLoanPercent);
                              require(params.amount <= maxLoanSizeStable, Errors.AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE);
                            }
                            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 interestRateMode The interest rate mode of the debt being repaid
                           * @param onBehalfOf The address of the user msg.sender is repaying for
                           * @param stableDebt The borrow balance of the user
                           * @param variableDebt The borrow balance of the user
                           */
                          function validateRepay(
                            DataTypes.ReserveCache memory reserveCache,
                            uint256 amountSent,
                            DataTypes.InterestRateMode interestRateMode,
                            address onBehalfOf,
                            uint256 stableDebt,
                            uint256 variableDebt
                          ) internal view {
                            require(amountSent != 0, Errors.INVALID_AMOUNT);
                            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(
                              (stableDebt != 0 && interestRateMode == DataTypes.InterestRateMode.STABLE) ||
                                (variableDebt != 0 && interestRateMode == DataTypes.InterestRateMode.VARIABLE),
                              Errors.NO_DEBT_OF_SELECTED_TYPE
                            );
                          }
                          /**
                           * @notice Validates a swap of borrow rate mode.
                           * @param reserve The reserve state on which the user is swapping the rate
                           * @param reserveCache The cached data of the reserve
                           * @param userConfig The user reserves configuration
                           * @param stableDebt The stable debt of the user
                           * @param variableDebt The variable debt of the user
                           * @param currentRateMode The rate mode of the debt being swapped
                           */
                          function validateSwapRateMode(
                            DataTypes.ReserveData storage reserve,
                            DataTypes.ReserveCache memory reserveCache,
                            DataTypes.UserConfigurationMap storage userConfig,
                            uint256 stableDebt,
                            uint256 variableDebt,
                            DataTypes.InterestRateMode currentRateMode
                          ) internal view {
                            (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = reserveCache
                              .reserveConfiguration
                              .getFlags();
                            require(isActive, Errors.RESERVE_INACTIVE);
                            require(!isPaused, Errors.RESERVE_PAUSED);
                            require(!isFrozen, Errors.RESERVE_FROZEN);
                            if (currentRateMode == DataTypes.InterestRateMode.STABLE) {
                              require(stableDebt != 0, Errors.NO_OUTSTANDING_STABLE_DEBT);
                            } else if (currentRateMode == DataTypes.InterestRateMode.VARIABLE) {
                              require(variableDebt != 0, Errors.NO_OUTSTANDING_VARIABLE_DEBT);
                              /**
                               * user wants to swap to stable, before swapping we need to ensure that
                               * 1. stable borrow rate is enabled on the reserve
                               * 2. user is not trying to abuse the reserve by supplying
                               * more collateral than he is borrowing, artificially lowering
                               * the interest rate, borrowing at variable, and switching to stable
                               */
                              require(stableRateEnabled, Errors.STABLE_BORROWING_NOT_ENABLED);
                              require(
                                !userConfig.isUsingAsCollateral(reserve.id) ||
                                  reserveCache.reserveConfiguration.getLtv() == 0 ||
                                  stableDebt + variableDebt > IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender),
                                Errors.COLLATERAL_SAME_AS_BORROWING_CURRENCY
                              );
                            } else {
                              revert(Errors.INVALID_INTEREST_RATE_MODE_SELECTED);
                            }
                          }
                          /**
                           * @notice Validates a stable borrow rate rebalance action.
                           * @dev Rebalancing is accepted when depositors are earning <= 90% of their earnings in pure supply/demand market (variable rate only)
                           * For this to be the case, there has to be quite large stable debt with an interest rate below the current variable rate.
                           * @param reserve The reserve state on which the user is getting rebalanced
                           * @param reserveCache The cached state of the reserve
                           * @param reserveAddress The address of the reserve
                           */
                          function validateRebalanceStableBorrowRate(
                            DataTypes.ReserveData storage reserve,
                            DataTypes.ReserveCache memory reserveCache,
                            address reserveAddress
                          ) internal view {
                            (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                            require(isActive, Errors.RESERVE_INACTIVE);
                            require(!isPaused, Errors.RESERVE_PAUSED);
                            uint256 totalDebt = IERC20(reserveCache.stableDebtTokenAddress).totalSupply() +
                              IERC20(reserveCache.variableDebtTokenAddress).totalSupply();
                            (uint256 liquidityRateVariableDebtOnly, , ) = IReserveInterestRateStrategy(
                              reserve.interestRateStrategyAddress
                            ).calculateInterestRates(
                                DataTypes.CalculateInterestRatesParams({
                                  unbacked: reserve.unbacked,
                                  liquidityAdded: 0,
                                  liquidityTaken: 0,
                                  totalStableDebt: 0,
                                  totalVariableDebt: totalDebt,
                                  averageStableBorrowRate: 0,
                                  reserveFactor: reserveCache.reserveFactor,
                                  reserve: reserveAddress,
                                  aToken: reserveCache.aTokenAddress
                                })
                              );
                            require(
                              reserveCache.currLiquidityRate <=
                                liquidityRateVariableDebtOnly.percentMul(REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD),
                              Errors.INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET
                            );
                          }
                          /**
                           * @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++) {
                              validateFlashloanSimple(reservesData[assets[i]]);
                            }
                          }
                          /**
                           * @notice Validates a flashloan action.
                           * @param reserve The state of the reserve
                           */
                          function validateFlashloanSimple(DataTypes.ReserveData storage reserve) 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);
                          }
                          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 params Additional parameters needed for the validation
                           */
                          function validateLiquidationCall(
                            DataTypes.UserConfigurationMap storage userConfig,
                            DataTypes.ReserveData storage collateralReserve,
                            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(
                              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.stableDebtTokenAddress).totalSupply() == 0, Errors.STABLE_DEBT_NOT_ZERO);
                            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 reservesData The state of all the reserves
                           * @param reservesList The addresses of all the active reserves
                           * @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(address => DataTypes.ReserveData) storage reservesData,
                            mapping(uint256 => address) storage reservesList,
                            mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                            DataTypes.UserConfigurationMap memory userConfig,
                            uint256 reservesCount,
                            uint8 categoryId
                          ) internal view {
                            // category is invalid if the liq threshold is not set
                            require(
                              categoryId == 0 || eModeCategories[categoryId].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)) {
                                    DataTypes.ReserveConfigurationMap memory configuration = reservesData[reservesList[i]]
                                      .configuration;
                                    require(
                                      configuration.getEModeCategory() == categoryId,
                                      Errors.INCONSISTENT_EMODE_CATEGORY
                                    );
                                  }
                                }
                              }
                            }
                          }
                          /**
                           * @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.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: 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: MIT
                        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: 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 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;
                            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 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.12;
                        import {VersionedInitializable} from '../libraries/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
                         *   # Swap their loans between variable and stable rate
                         *   # Enable/disable their supplied assets as collateral rebalance stable rate borrow positions
                         *   # 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
                         */
                        contract Pool is VersionedInitializable, PoolStorage, IPool {
                          using ReserveLogic for DataTypes.ReserveData;
                          uint256 public constant POOL_REVISION = 0x5;
                          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
                            );
                          }
                          function getRevision() internal pure virtual override returns (uint256) {
                            return POOL_REVISION;
                          }
                          /**
                           * @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) public virtual initializer {
                            require(provider == ADDRESSES_PROVIDER, Errors.INVALID_ADDRESSES_PROVIDER);
                            _maxStableRateBorrowSizePercent = 0.25e4;
                          }
                          /// @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 {
                            IERC20WithPermit(asset).permit(
                              msg.sender,
                              address(this),
                              amount,
                              deadline,
                              permitV,
                              permitR,
                              permitS
                            );
                            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,
                                maxStableRateBorrowSizePercent: _maxStableRateBorrowSizePercent,
                                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) {
                            {
                              IERC20WithPermit(asset).permit(
                                msg.sender,
                                address(this),
                                amount,
                                deadline,
                                permitV,
                                permitR,
                                permitS
                              );
                            }
                            {
                              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 swapBorrowRateMode(address asset, uint256 interestRateMode) public virtual override {
                            BorrowLogic.executeSwapBorrowRateMode(
                              _reserves[asset],
                              _usersConfig[msg.sender],
                              asset,
                              DataTypes.InterestRateMode(interestRateMode)
                            );
                          }
                          /// @inheritdoc IPool
                          function rebalanceStableBorrowRate(address asset, address user) public virtual override {
                            BorrowLogic.executeRebalanceStableBorrowRate(_reserves[asset], asset, user);
                          }
                          /// @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,
                              maxStableRateBorrowSizePercent: _maxStableRateBorrowSizePercent,
                              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 getReserveData(
                            address asset
                          ) external view virtual override returns (DataTypes.ReserveData memory) {
                            return _reserves[asset];
                          }
                          /// @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 MAX_STABLE_RATE_BORROW_SIZE_PERCENT() public view virtual override returns (uint256) {
                            return _maxStableRateBorrowSizePercent;
                          }
                          /// @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 stableDebtAddress,
                            address variableDebtAddress,
                            address interestRateStrategyAddress
                          ) external virtual override onlyPoolConfigurator {
                            if (
                              PoolLogic.executeInitReserve(
                                _reserves,
                                _reservesList,
                                DataTypes.InitReserveParams({
                                  asset: asset,
                                  aTokenAddress: aTokenAddress,
                                  stableDebtAddress: stableDebtAddress,
                                  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 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.EModeCategory 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] = category;
                          }
                          /// @inheritdoc IPool
                          function getEModeCategoryData(
                            uint8 id
                          ) external view virtual override returns (DataTypes.EModeCategory memory) {
                            return _eModeCategories[id];
                          }
                          /// @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 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
                              })
                            );
                          }
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        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;
                          // Available liquidity that can be borrowed at once at stable rate, expressed in bps
                          uint64 internal _maxStableRateBorrowSizePercent;
                          // Maximum number of active reserves there have been in the protocol. It is the upper bound of the reserves list
                          uint16 internal _reservesCount;
                          address public expressRelay;
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                        import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {IERC20Detailed} from '../../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                        import {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;
                          }
                        }
                        

                        File 6 of 15: PoolAddressesProvider
                        // 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.12;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                          function _msgSender() internal view virtual returns (address payable) {
                            return 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.12;
                        import './Context.sol';
                        /**
                         * @dev Contract module which provides a basic access control mechanism, where
                         * there is an account (an owner) that can be granted exclusive access to
                         * specific functions.
                         *
                         * By default, the owner account will be the one that deploys the contract. This
                         * can later be changed with {transferOwnership}.
                         *
                         * This module is used through inheritance. It will make available the modifier
                         * `onlyOwner`, which can be applied to your functions to restrict their use to
                         * the owner.
                         */
                        contract Ownable is Context {
                          address private _owner;
                          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                          /**
                           * @dev Initializes the contract setting the deployer as the initial owner.
                           */
                          constructor() {
                            address msgSender = _msgSender();
                            _owner = msgSender;
                            emit OwnershipTransferred(address(0), msgSender);
                          }
                          /**
                           * @dev Returns the address of the current owner.
                           */
                          function owner() public view returns (address) {
                            return _owner;
                          }
                          /**
                           * @dev Throws if called by any account other than the owner.
                           */
                          modifier onlyOwner() {
                            require(_owner == _msgSender(), 'Ownable: caller is not the owner');
                            _;
                          }
                          /**
                           * @dev Leaves the contract without owner. It will not be possible to call
                           * `onlyOwner` functions anymore. Can only be called by the current owner.
                           *
                           * NOTE: Renouncing ownership will leave the contract without an owner,
                           * thereby removing any functionality that is only available to the owner.
                           */
                          function renounceOwnership() public virtual onlyOwner {
                            emit OwnershipTransferred(_owner, address(0));
                            _owner = address(0);
                          }
                          /**
                           * @dev Transfers ownership of the contract to a new account (`newOwner`).
                           * Can only be called by the current owner.
                           */
                          function transferOwnership(address newOwner) public virtual onlyOwner {
                            require(newOwner != address(0), 'Ownable: new owner is the zero address');
                            emit OwnershipTransferred(_owner, newOwner);
                            _owner = newOwner;
                          }
                        }
                        // SPDX-License-Identifier: AGPL-3.0
                        pragma solidity 0.8.12;
                        import './Proxy.sol';
                        import '../contracts/Address.sol';
                        /**
                         * @title BaseUpgradeabilityProxy
                         * @dev This contract implements a proxy that allows to change the
                         * implementation address to which it will delegate.
                         * Such a change is called an implementation upgrade.
                         */
                        contract BaseUpgradeabilityProxy is Proxy {
                          /**
                           * @dev Emitted when the implementation is upgraded.
                           * @param implementation Address of the new implementation.
                           */
                          event Upgraded(address indexed implementation);
                          /**
                           * @dev Storage slot with the address of the current implementation.
                           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                           * validated in the constructor.
                           */
                          bytes32 internal constant IMPLEMENTATION_SLOT =
                            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                          /**
                           * @dev Returns the current implementation.
                           * @return impl Address of the current implementation
                           */
                          function _implementation() internal 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.12;
                        import './BaseUpgradeabilityProxy.sol';
                        /**
                         * @title InitializableUpgradeabilityProxy
                         * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                         * implementation and init data.
                         */
                        contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                          /**
                           * @dev Contract initializer.
                           * @param _logic Address of the initial implementation.
                           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                           * It should include the signature and the parameters of the function to be called, as described in
                           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                           */
                          function initialize(address _logic, bytes memory _data) public payable {
                            require(_implementation() == address(0));
                            assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                            _setImplementation(_logic);
                            if (_data.length > 0) {
                              (bool success, ) = _logic.delegatecall(_data);
                              require(success);
                            }
                          }
                        }
                        // SPDX-License-Identifier: AGPL-3.0
                        pragma solidity 0.8.12;
                        /**
                         * @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.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: BUSL-1.1
                        pragma solidity 0.8.12;
                        import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
                        import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol';
                        import {InitializableImmutableAdminUpgradeabilityProxy} from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol';
                        /**
                         * @title PoolAddressesProvider
                         * @author Aave
                         * @notice Main registry of addresses part of or connected to the protocol, including permissioned roles
                         * @dev Acts as factory of proxies and admin of those, so with right to change its implementations
                         * @dev Owned by the Aave Governance
                         */
                        contract PoolAddressesProvider is Ownable, IPoolAddressesProvider {
                          // Identifier of the Aave Market
                          string private _marketId;
                          // Map of registered addresses (identifier => registeredAddress)
                          mapping(bytes32 => address) private _addresses;
                          // Main identifiers
                          bytes32 private constant POOL = 'POOL';
                          bytes32 private constant POOL_CONFIGURATOR = 'POOL_CONFIGURATOR';
                          bytes32 private constant PRICE_ORACLE = 'PRICE_ORACLE';
                          bytes32 private constant ACL_MANAGER = 'ACL_MANAGER';
                          bytes32 private constant ACL_ADMIN = 'ACL_ADMIN';
                          bytes32 private constant PRICE_ORACLE_SENTINEL = 'PRICE_ORACLE_SENTINEL';
                          bytes32 private constant DATA_PROVIDER = 'DATA_PROVIDER';
                          /**
                           * @dev Constructor.
                           * @param marketId The identifier of the market.
                           * @param owner The owner address of this contract.
                           */
                          constructor(string memory marketId, address owner) {
                            _setMarketId(marketId);
                            transferOwnership(owner);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getMarketId() external view override returns (string memory) {
                            return _marketId;
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setMarketId(string memory newMarketId) external override onlyOwner {
                            _setMarketId(newMarketId);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getAddress(bytes32 id) public view override returns (address) {
                            return _addresses[id];
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setAddress(bytes32 id, address newAddress) external override onlyOwner {
                            address oldAddress = _addresses[id];
                            _addresses[id] = newAddress;
                            emit AddressSet(id, oldAddress, newAddress);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setAddressAsProxy(
                            bytes32 id,
                            address newImplementationAddress
                          ) external override onlyOwner {
                            address proxyAddress = _addresses[id];
                            address oldImplementationAddress = _getProxyImplementation(id);
                            _updateImpl(id, newImplementationAddress);
                            emit AddressSetAsProxy(id, proxyAddress, oldImplementationAddress, newImplementationAddress);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getPool() external view override returns (address) {
                            return getAddress(POOL);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setPoolImpl(address newPoolImpl) external override onlyOwner {
                            address oldPoolImpl = _getProxyImplementation(POOL);
                            _updateImpl(POOL, newPoolImpl);
                            emit PoolUpdated(oldPoolImpl, newPoolImpl);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getPoolConfigurator() external view override returns (address) {
                            return getAddress(POOL_CONFIGURATOR);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external override onlyOwner {
                            address oldPoolConfiguratorImpl = _getProxyImplementation(POOL_CONFIGURATOR);
                            _updateImpl(POOL_CONFIGURATOR, newPoolConfiguratorImpl);
                            emit PoolConfiguratorUpdated(oldPoolConfiguratorImpl, newPoolConfiguratorImpl);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getPriceOracle() external view override returns (address) {
                            return getAddress(PRICE_ORACLE);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setPriceOracle(address newPriceOracle) external override onlyOwner {
                            address oldPriceOracle = _addresses[PRICE_ORACLE];
                            _addresses[PRICE_ORACLE] = newPriceOracle;
                            emit PriceOracleUpdated(oldPriceOracle, newPriceOracle);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getACLManager() external view override returns (address) {
                            return getAddress(ACL_MANAGER);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setACLManager(address newAclManager) external override onlyOwner {
                            address oldAclManager = _addresses[ACL_MANAGER];
                            _addresses[ACL_MANAGER] = newAclManager;
                            emit ACLManagerUpdated(oldAclManager, newAclManager);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getACLAdmin() external view override returns (address) {
                            return getAddress(ACL_ADMIN);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setACLAdmin(address newAclAdmin) external override onlyOwner {
                            address oldAclAdmin = _addresses[ACL_ADMIN];
                            _addresses[ACL_ADMIN] = newAclAdmin;
                            emit ACLAdminUpdated(oldAclAdmin, newAclAdmin);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getPriceOracleSentinel() external view override returns (address) {
                            return getAddress(PRICE_ORACLE_SENTINEL);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setPriceOracleSentinel(address newPriceOracleSentinel) external override onlyOwner {
                            address oldPriceOracleSentinel = _addresses[PRICE_ORACLE_SENTINEL];
                            _addresses[PRICE_ORACLE_SENTINEL] = newPriceOracleSentinel;
                            emit PriceOracleSentinelUpdated(oldPriceOracleSentinel, newPriceOracleSentinel);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function getPoolDataProvider() external view override returns (address) {
                            return getAddress(DATA_PROVIDER);
                          }
                          /// @inheritdoc IPoolAddressesProvider
                          function setPoolDataProvider(address newDataProvider) external override onlyOwner {
                            address oldDataProvider = _addresses[DATA_PROVIDER];
                            _addresses[DATA_PROVIDER] = newDataProvider;
                            emit PoolDataProviderUpdated(oldDataProvider, newDataProvider);
                          }
                          /**
                           * @notice Internal function to update the implementation of a specific proxied component of the protocol.
                           * @dev If there is no proxy registered with the given identifier, it creates the proxy setting `newAddress`
                           *   as implementation and calls the initialize() function on the proxy
                           * @dev If there is already a proxy registered, it just updates the implementation to `newAddress` and
                           *   calls the initialize() function via upgradeToAndCall() in the proxy
                           * @param id The id of the proxy to be updated
                           * @param newAddress The address of the new implementation
                           */
                          function _updateImpl(bytes32 id, address newAddress) internal {
                            address proxyAddress = _addresses[id];
                            InitializableImmutableAdminUpgradeabilityProxy proxy;
                            bytes memory params = abi.encodeWithSignature('initialize(address)', address(this));
                            if (proxyAddress == address(0)) {
                              proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this));
                              _addresses[id] = proxyAddress = address(proxy);
                              proxy.initialize(newAddress, params);
                              emit ProxyCreated(id, proxyAddress, newAddress);
                            } else {
                              proxy = InitializableImmutableAdminUpgradeabilityProxy(payable(proxyAddress));
                              proxy.upgradeToAndCall(newAddress, params);
                            }
                          }
                          /**
                           * @notice Updates the identifier of the Aave market.
                           * @param newMarketId The new id of the market
                           */
                          function _setMarketId(string memory newMarketId) internal {
                            string memory oldMarketId = _marketId;
                            _marketId = newMarketId;
                            emit MarketIdSet(oldMarketId, newMarketId);
                          }
                          /**
                           * @notice Returns the the implementation contract of the proxy contract by its identifier.
                           * @dev It returns ZERO if there is no registered address with the given id
                           * @dev It reverts if the registered address with the given id is not `InitializableImmutableAdminUpgradeabilityProxy`
                           * @param id The id
                           * @return The address of the implementation contract
                           */
                          function _getProxyImplementation(bytes32 id) internal returns (address) {
                            address proxyAddress = _addresses[id];
                            if (proxyAddress == address(0)) {
                              return address(0);
                            } else {
                              address payable payableProxyAddress = payable(proxyAddress);
                              return InitializableImmutableAdminUpgradeabilityProxy(payableProxyAddress).implementation();
                            }
                          }
                        }
                        // SPDX-License-Identifier: AGPL-3.0
                        pragma solidity 0.8.12;
                        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.12;
                        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 15: SupplyLogic
                        // SPDX-License-Identifier: LGPL-3.0-or-later
                        pragma solidity 0.8.12;
                        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.12;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                          function _msgSender() internal view virtual returns (address payable) {
                            return 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.12;
                        /**
                         * @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: AGPL-3.0
                        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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import {IERC20} from './IERC20.sol';
                        interface IERC20Detailed is IERC20 {
                          function name() external view returns (string memory);
                          function symbol() external view returns (string memory);
                          function decimals() external view returns (uint8);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                        pragma solidity 0.8.12;
                        /**
                         * @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 {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;
                        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://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 -> 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://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 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 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 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 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: AGPL-3.0
                        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: AGPL-3.0
                        pragma solidity ^0.8.0;
                        import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                        /**
                         * @title IReserveInterestRateStrategy
                         * @author Aave
                         * @notice Interface for the calculation of the interest rates
                         */
                        interface IReserveInterestRateStrategy {
                          /**
                           * @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 rays
                           * @return stableBorrowRate The stable borrow rate expressed in rays
                           * @return variableBorrowRate The variable borrow rate expressed in rays
                           */
                          function calculateInterestRates(
                            DataTypes.CalculateInterestRatesParams memory params
                          ) external view returns (uint256, uint256, uint256);
                        }
                        // 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 {IInitializableDebtToken} from './IInitializableDebtToken.sol';
                        /**
                         * @title IStableDebtToken
                         * @author Aave
                         * @notice Defines the interface for the stable debt token
                         * @dev It does not inherit from IERC20 to save in code size
                         */
                        interface IStableDebtToken is IInitializableDebtToken {
                          /**
                           * @dev Emitted when new stable debt is minted
                           * @param user The address of the user who triggered the minting
                           * @param onBehalfOf The recipient of stable debt tokens
                           * @param amount The amount minted (user entered amount + balance increase from interest)
                           * @param currentBalance The balance of the user based on the previous balance and balance increase from interest
                           * @param balanceIncrease The increase in balance since the last action of the user 'onBehalfOf'
                           * @param newRate The rate of the debt after the minting
                           * @param avgStableRate The next average stable rate after the minting
                           * @param newTotalSupply The next total supply of the stable debt token after the action
                           */
                          event Mint(
                            address indexed user,
                            address indexed onBehalfOf,
                            uint256 amount,
                            uint256 currentBalance,
                            uint256 balanceIncrease,
                            uint256 newRate,
                            uint256 avgStableRate,
                            uint256 newTotalSupply
                          );
                          /**
                           * @dev Emitted when new stable debt is burned
                           * @param from The address from which the debt will be burned
                           * @param amount The amount being burned (user entered amount - balance increase from interest)
                           * @param currentBalance The balance of the user based on the previous balance and balance increase from interest
                           * @param balanceIncrease The increase in balance since the last action of 'from'
                           * @param avgStableRate The next average stable rate after the burning
                           * @param newTotalSupply The next total supply of the stable debt token after the action
                           */
                          event Burn(
                            address indexed from,
                            uint256 amount,
                            uint256 currentBalance,
                            uint256 balanceIncrease,
                            uint256 avgStableRate,
                            uint256 newTotalSupply
                          );
                          /**
                           * @notice Mints debt token to the `onBehalfOf` address.
                           * @dev The resulting rate is the weighted average between the rate of the new debt
                           * and the rate of the previous debt
                           * @param user The address receiving the borrowed underlying, being the delegatee in case
                           * of credit delegate, or same as `onBehalfOf` otherwise
                           * @param onBehalfOf The address receiving the debt tokens
                           * @param amount The amount of debt tokens to mint
                           * @param rate The rate of the debt being minted
                           * @return True if it is the first borrow, false otherwise
                           * @return The total stable debt
                           * @return The average stable borrow rate
                           */
                          function mint(
                            address user,
                            address onBehalfOf,
                            uint256 amount,
                            uint256 rate
                          ) external returns (bool, uint256, uint256);
                          /**
                           * @notice Burns debt of `user`
                           * @dev The resulting rate is the weighted average between the rate of the new debt
                           * and the rate of the previous debt
                           * @dev In some instances, a burn transaction will emit a mint event
                           * if the amount to burn is less than the interest the user earned
                           * @param from The address from which the debt will be burned
                           * @param amount The amount of debt tokens getting burned
                           * @return The total stable debt
                           * @return The average stable borrow rate
                           */
                          function burn(address from, uint256 amount) external returns (uint256, uint256);
                          /**
                           * @notice Returns the average rate of all the stable rate loans.
                           * @return The average stable rate
                           */
                          function getAverageStableRate() external view returns (uint256);
                          /**
                           * @notice Returns the stable rate of the user debt
                           * @param user The address of the user
                           * @return The stable rate of the user
                           */
                          function getUserStableRate(address user) external view returns (uint256);
                          /**
                           * @notice Returns the timestamp of the last update of the user
                           * @param user The address of the user
                           * @return The timestamp
                           */
                          function getUserLastUpdated(address user) external view returns (uint40);
                          /**
                           * @notice Returns the principal, the total supply, the average stable rate and the timestamp for the last update
                           * @return The principal
                           * @return The total supply
                           * @return The average stable rate
                           * @return The timestamp of the last update
                           */
                          function getSupplyData() external view returns (uint256, uint256, uint256, uint40);
                          /**
                           * @notice Returns the timestamp of the last update of the total supply
                           * @return The timestamp
                           */
                          function getTotalSupplyLastUpdated() external view returns (uint40);
                          /**
                           * @notice Returns the total supply and the average stable rate
                           * @return The total supply
                           * @return The average rate
                           */
                          function getTotalSupplyAndAvgRate() external view returns (uint256, uint256);
                          /**
                           * @notice Returns the principal debt balance of the user
                           * @return The debt balance of the user since the last burn/mint action
                           */
                          function principalBalanceOf(address user) external view returns (uint256);
                          /**
                           * @notice Returns the address of the underlying asset of this stableDebtToken (E.g. WETH for stableDebtWETH)
                           * @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.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 {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 STABLE_BORROWING_MASK =          0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // 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
                          uint256 internal constant EMODE_CATEGORY_MASK =            0xFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                          uint256 internal constant UNBACKED_MINT_CAP_MASK =         0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore
                          uint256 internal constant DEBT_CEILING_MASK =              0xF0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 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 STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59;
                          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;
                          uint256 internal constant EMODE_CATEGORY_START_BIT_POSITION = 168;
                          uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176;
                          uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212;
                          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_EMODE_CATEGORY = 255;
                          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 Enables or disables stable rate borrowing on the reserve
                           * @param self The reserve configuration
                           * @param enabled True if the stable rate borrowing needs to be enabled, false otherwise
                           */
                          function setStableRateBorrowingEnabled(
                            DataTypes.ReserveConfigurationMap memory self,
                            bool enabled
                          ) internal pure {
                            self.data =
                              (self.data & STABLE_BORROWING_MASK) |
                              (uint256(enabled ? 1 : 0) << STABLE_BORROWING_ENABLED_START_BIT_POSITION);
                          }
                          /**
                           * @notice Gets the stable rate borrowing state of the reserve
                           * @param self The reserve configuration
                           * @return The stable rate borrowing state
                           */
                          function getStableRateBorrowingEnabled(
                            DataTypes.ReserveConfigurationMap memory self
                          ) internal pure returns (bool) {
                            return (self.data & ~STABLE_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 eMode asset category
                           * @param self The reserve configuration
                           * @param category The asset category when the user selects the eMode
                           */
                          function setEModeCategory(
                            DataTypes.ReserveConfigurationMap memory self,
                            uint256 category
                          ) internal pure {
                            require(category <= MAX_VALID_EMODE_CATEGORY, Errors.INVALID_EMODE_CATEGORY);
                            self.data = (self.data & EMODE_CATEGORY_MASK) | (category << EMODE_CATEGORY_START_BIT_POSITION);
                          }
                          /**
                           * @dev Gets the eMode asset category
                           * @param self The reserve configuration
                           * @return The eMode category for the asset
                           */
                          function getEModeCategory(
                            DataTypes.ReserveConfigurationMap memory self
                          ) internal pure returns (uint256) {
                            return (self.data & ~EMODE_CATEGORY_MASK) >> EMODE_CATEGORY_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 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 stableRateBorrowing enabled
                           * @return The state flag representing paused
                           */
                          function getFlags(
                            DataTypes.ReserveConfigurationMap memory self
                          ) internal pure returns (bool, bool, bool, bool, bool) {
                            uint256 dataLocal = self.data;
                            return (
                              (dataLocal & ~ACTIVE_MASK) != 0,
                              (dataLocal & ~FROZEN_MASK) != 0,
                              (dataLocal & ~BORROWING_MASK) != 0,
                              (dataLocal & ~STABLE_BORROWING_MASK) != 0,
                              (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
                           * @return The state param representing eMode category
                           */
                          function getParams(
                            DataTypes.ReserveConfigurationMap memory self
                          ) internal pure returns (uint256, 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,
                              (dataLocal & ~EMODE_CATEGORY_MASK) >> EMODE_CATEGORY_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.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: 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_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 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: MIT
                        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 {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 {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 {
                            ValidationLogic.validateSetUserEMode(
                              reservesData,
                              reservesList,
                              eModeCategories,
                              userConfig,
                              params.reservesCount,
                              params.categoryId
                            );
                            uint8 prevCategoryId = usersEModeCategory[msg.sender];
                            usersEModeCategory[msg.sender] = params.categoryId;
                            if (prevCategoryId != 0) {
                              ValidationLogic.validateHealthFactor(
                                reservesData,
                                reservesList,
                                eModeCategories,
                                userConfig,
                                msg.sender,
                                params.categoryId,
                                params.reservesCount,
                                params.oracle
                              );
                            }
                            emit UserEModeSet(msg.sender, params.categoryId);
                          }
                          /**
                           * @notice Gets the eMode configuration and calculates the eMode asset price if a custom oracle is configured
                           * @dev The eMode asset price returned is 0 if no oracle is specified
                           * @param category The user eMode category
                           * @param oracle The price oracle
                           * @return The eMode ltv
                           * @return The eMode liquidation threshold
                           * @return The eMode asset price
                           */
                          function getEModeConfiguration(
                            DataTypes.EModeCategory storage category,
                            IPriceOracleGetter oracle
                          ) internal view returns (uint256, uint256, uint256) {
                            uint256 eModeAssetPrice = 0;
                            address eModePriceSource = category.priceSource;
                            if (eModePriceSource != address(0)) {
                              eModeAssetPrice = oracle.getAssetPrice(eModePriceSource);
                            }
                            return (category.ltv, category.liquidationThreshold, eModeAssetPrice);
                          }
                          /**
                           * @notice Checks if eMode is active for a user and if yes, if the asset belongs to the eMode category chosen
                           * @param eModeUserCategory The user eMode category
                           * @param eModeAssetCategory The asset eMode category
                           * @return True if eMode is active and the asset belongs to the eMode category chosen by the user, false otherwise
                           */
                          function isInEModeCategory(
                            uint256 eModeUserCategory,
                            uint256 eModeAssetCategory
                          ) internal pure returns (bool) {
                            return (eModeUserCategory != 0 && eModeAssetCategory == eModeUserCategory);
                          }
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        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 {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 eModeAssetPrice;
                            uint256 eModeLtv;
                            uint256 eModeLiqThreshold;
                            uint256 eModeAssetCategory;
                            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, vars.eModeLiqThreshold, vars.eModeAssetPrice) = EModeLogic
                                .getEModeConfiguration(
                                  eModeCategories[params.userEModeCategory],
                                  IPriceOracleGetter(params.oracle)
                                );
                            }
                            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,
                                ,
                                vars.eModeAssetCategory
                              ) = currentReserve.configuration.getParams();
                              unchecked {
                                vars.assetUnit = 10 ** vars.decimals;
                              }
                              vars.assetPrice = vars.eModeAssetPrice != 0 &&
                                params.userEModeCategory == vars.eModeAssetCategory
                                ? vars.eModeAssetPrice
                                : 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 = EModeLogic.isInEModeCategory(
                                  params.userEModeCategory,
                                  vars.eModeAssetCategory
                                );
                                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 stable and variable debt tokens 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) {
                              userTotalDebt = userTotalDebt.rayMul(reserve.getNormalizedDebt());
                            }
                            userTotalDebt = userTotalDebt + IERC20(reserve.stableDebtTokenAddress).balanceOf(user);
                            userTotalDebt = assetPrice * userTotalDebt;
                            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: BUSL-1.1
                        pragma solidity 0.8.12;
                        import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
                        import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol';
                        import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
                        import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
                        import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
                        import {MathUtils} from '../math/MathUtils.sol';
                        import {WadRayMath} from '../math/WadRayMath.sol';
                        import {PercentageMath} from '../math/PercentageMath.sol';
                        import {Errors} from '../helpers/Errors.sol';
                        import {DataTypes} from '../types/DataTypes.sol';
                        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 stableDebtTokenAddress The address of the overlying stable debt token 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 stableDebtTokenAddress,
                            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.stableDebtTokenAddress = stableDebtTokenAddress;
                            reserve.variableDebtTokenAddress = variableDebtTokenAddress;
                            reserve.interestRateStrategyAddress = interestRateStrategyAddress;
                          }
                          struct UpdateInterestRatesLocalVars {
                            uint256 nextLiquidityRate;
                            uint256 nextStableRate;
                            uint256 nextVariableRate;
                            uint256 totalVariableDebt;
                          }
                          /**
                           * @notice Updates the reserve current stable borrow rate, the 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 updateInterestRates(
                            DataTypes.ReserveData storage reserve,
                            DataTypes.ReserveCache memory reserveCache,
                            address reserveAddress,
                            uint256 liquidityAdded,
                            uint256 liquidityTaken
                          ) internal {
                            UpdateInterestRatesLocalVars memory vars;
                            vars.totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul(
                              reserveCache.nextVariableBorrowIndex
                            );
                            (
                              vars.nextLiquidityRate,
                              vars.nextStableRate,
                              vars.nextVariableRate
                            ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates(
                              DataTypes.CalculateInterestRatesParams({
                                unbacked: reserve.unbacked,
                                liquidityAdded: liquidityAdded,
                                liquidityTaken: liquidityTaken,
                                totalStableDebt: reserveCache.nextTotalStableDebt,
                                totalVariableDebt: vars.totalVariableDebt,
                                averageStableBorrowRate: reserveCache.nextAvgStableBorrowRate,
                                reserveFactor: reserveCache.reserveFactor,
                                reserve: reserveAddress,
                                aToken: reserveCache.aTokenAddress
                              })
                            );
                            reserve.currentLiquidityRate = vars.nextLiquidityRate.toUint128();
                            reserve.currentStableBorrowRate = vars.nextStableRate.toUint128();
                            reserve.currentVariableBorrowRate = vars.nextVariableRate.toUint128();
                            emit ReserveDataUpdated(
                              reserveAddress,
                              vars.nextLiquidityRate,
                              vars.nextStableRate,
                              vars.nextVariableRate,
                              reserveCache.nextLiquidityIndex,
                              reserveCache.nextVariableBorrowIndex
                            );
                          }
                          struct AccrueToTreasuryLocalVars {
                            uint256 prevTotalStableDebt;
                            uint256 prevTotalVariableDebt;
                            uint256 currTotalVariableDebt;
                            uint256 cumulatedStableInterest;
                            uint256 totalDebtAccrued;
                            uint256 amountToMint;
                          }
                          /**
                           * @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 {
                            AccrueToTreasuryLocalVars memory vars;
                            if (reserveCache.reserveFactor == 0) {
                              return;
                            }
                            //calculate the total variable debt at moment of the last interaction
                            vars.prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                              reserveCache.currVariableBorrowIndex
                            );
                            //calculate the new total variable debt after accumulation of the interest on the index
                            vars.currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul(
                              reserveCache.nextVariableBorrowIndex
                            );
                            //calculate the stable debt until the last timestamp update
                            vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest(
                              reserveCache.currAvgStableBorrowRate,
                              reserveCache.stableDebtLastUpdateTimestamp,
                              reserveCache.reserveLastUpdateTimestamp
                            );
                            vars.prevTotalStableDebt = reserveCache.currPrincipalStableDebt.rayMul(
                              vars.cumulatedStableInterest
                            );
                            //debt accrued is the sum of the current debt minus the sum of the debt at the last update
                            vars.totalDebtAccrued =
                              vars.currTotalVariableDebt +
                              reserveCache.currTotalStableDebt -
                              vars.prevTotalVariableDebt -
                              vars.prevTotalStableDebt;
                            vars.amountToMint = vars.totalDebtAccrued.percentMul(reserveCache.reserveFactor);
                            if (vars.amountToMint != 0) {
                              reserve.accruedToTreasury += vars
                                .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.stableDebtTokenAddress = reserve.stableDebtTokenAddress;
                            reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress;
                            reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp;
                            reserveCache.currScaledVariableDebt = reserveCache.nextScaledVariableDebt = IVariableDebtToken(
                              reserveCache.variableDebtTokenAddress
                            ).scaledTotalSupply();
                            (
                              reserveCache.currPrincipalStableDebt,
                              reserveCache.currTotalStableDebt,
                              reserveCache.currAvgStableBorrowRate,
                              reserveCache.stableDebtLastUpdateTimestamp
                            ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData();
                            // by default the actions are considered as not affecting the debt balances.
                            // if the action involves mint/burn of debt, the cache needs to be updated
                            reserveCache.nextTotalStableDebt = reserveCache.currTotalStableDebt;
                            reserveCache.nextAvgStableBorrowRate = reserveCache.currAvgStableBorrowRate;
                            return reserveCache;
                          }
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        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);
                            reserve.updateInterestRates(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();
                            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.updateInterestRates(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;
                            if (params.from != params.to && params.amount != 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.12;
                        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 {IStableDebtToken} from '../../../interfaces/IStableDebtToken.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 {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
                          ) 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);
                            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 eModePriceSource;
                            address siloedBorrowingAddress;
                            bool isActive;
                            bool isFrozen;
                            bool isPaused;
                            bool borrowingEnabled;
                            bool stableRateBorrowingEnabled;
                            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.stableRateBorrowingEnabled,
                              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.priceOracleSentinel == address(0) ||
                                IPriceOracleSentinel(params.priceOracleSentinel).isBorrowAllowed(),
                              Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
                            );
                            //validate interest rate mode
                            require(
                              params.interestRateMode == DataTypes.InterestRateMode.VARIABLE ||
                                params.interestRateMode == DataTypes.InterestRateMode.STABLE,
                              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 =
                                params.reserveCache.currTotalStableDebt +
                                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(
                                params.reserveCache.reserveConfiguration.getEModeCategory() == params.userEModeCategory,
                                Errors.INCONSISTENT_EMODE_CATEGORY
                              );
                              vars.eModePriceSource = eModeCategories[params.userEModeCategory].priceSource;
                            }
                            (
                              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(
                                vars.eModePriceSource != address(0) ? vars.eModePriceSource : 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
                            );
                            /**
                             * Following conditions need to be met if the user is borrowing at a stable rate:
                             * 1. Reserve must be enabled for stable rate borrowing
                             * 2. Users cannot borrow from the reserve if their collateral is (mostly) the same currency
                             *    they are borrowing, to prevent abuses.
                             * 3. Users will be able to borrow only a portion of the total available liquidity
                             */
                            if (params.interestRateMode == DataTypes.InterestRateMode.STABLE) {
                              //check if the borrow mode is stable and if stable rate borrowing is enabled on this reserve
                              require(vars.stableRateBorrowingEnabled, Errors.STABLE_BORROWING_NOT_ENABLED);
                              require(
                                !params.userConfig.isUsingAsCollateral(reservesData[params.asset].id) ||
                                  params.reserveCache.reserveConfiguration.getLtv() == 0 ||
                                  params.amount > IERC20(params.reserveCache.aTokenAddress).balanceOf(params.userAddress),
                                Errors.COLLATERAL_SAME_AS_BORROWING_CURRENCY
                              );
                              vars.availableLiquidity = IERC20(params.asset).balanceOf(params.reserveCache.aTokenAddress);
                              //calculate the max available loan size in stable rate mode as a percentage of the
                              //available liquidity
                              uint256 maxLoanSizeStable = vars.availableLiquidity.percentMul(params.maxStableLoanPercent);
                              require(params.amount <= maxLoanSizeStable, Errors.AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE);
                            }
                            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 interestRateMode The interest rate mode of the debt being repaid
                           * @param onBehalfOf The address of the user msg.sender is repaying for
                           * @param stableDebt The borrow balance of the user
                           * @param variableDebt The borrow balance of the user
                           */
                          function validateRepay(
                            DataTypes.ReserveCache memory reserveCache,
                            uint256 amountSent,
                            DataTypes.InterestRateMode interestRateMode,
                            address onBehalfOf,
                            uint256 stableDebt,
                            uint256 variableDebt
                          ) internal view {
                            require(amountSent != 0, Errors.INVALID_AMOUNT);
                            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(
                              (stableDebt != 0 && interestRateMode == DataTypes.InterestRateMode.STABLE) ||
                                (variableDebt != 0 && interestRateMode == DataTypes.InterestRateMode.VARIABLE),
                              Errors.NO_DEBT_OF_SELECTED_TYPE
                            );
                          }
                          /**
                           * @notice Validates a swap of borrow rate mode.
                           * @param reserve The reserve state on which the user is swapping the rate
                           * @param reserveCache The cached data of the reserve
                           * @param userConfig The user reserves configuration
                           * @param stableDebt The stable debt of the user
                           * @param variableDebt The variable debt of the user
                           * @param currentRateMode The rate mode of the debt being swapped
                           */
                          function validateSwapRateMode(
                            DataTypes.ReserveData storage reserve,
                            DataTypes.ReserveCache memory reserveCache,
                            DataTypes.UserConfigurationMap storage userConfig,
                            uint256 stableDebt,
                            uint256 variableDebt,
                            DataTypes.InterestRateMode currentRateMode
                          ) internal view {
                            (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = reserveCache
                              .reserveConfiguration
                              .getFlags();
                            require(isActive, Errors.RESERVE_INACTIVE);
                            require(!isPaused, Errors.RESERVE_PAUSED);
                            require(!isFrozen, Errors.RESERVE_FROZEN);
                            if (currentRateMode == DataTypes.InterestRateMode.STABLE) {
                              require(stableDebt != 0, Errors.NO_OUTSTANDING_STABLE_DEBT);
                            } else if (currentRateMode == DataTypes.InterestRateMode.VARIABLE) {
                              require(variableDebt != 0, Errors.NO_OUTSTANDING_VARIABLE_DEBT);
                              /**
                               * user wants to swap to stable, before swapping we need to ensure that
                               * 1. stable borrow rate is enabled on the reserve
                               * 2. user is not trying to abuse the reserve by supplying
                               * more collateral than he is borrowing, artificially lowering
                               * the interest rate, borrowing at variable, and switching to stable
                               */
                              require(stableRateEnabled, Errors.STABLE_BORROWING_NOT_ENABLED);
                              require(
                                !userConfig.isUsingAsCollateral(reserve.id) ||
                                  reserveCache.reserveConfiguration.getLtv() == 0 ||
                                  stableDebt + variableDebt > IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender),
                                Errors.COLLATERAL_SAME_AS_BORROWING_CURRENCY
                              );
                            } else {
                              revert(Errors.INVALID_INTEREST_RATE_MODE_SELECTED);
                            }
                          }
                          /**
                           * @notice Validates a stable borrow rate rebalance action.
                           * @dev Rebalancing is accepted when depositors are earning <= 90% of their earnings in pure supply/demand market (variable rate only)
                           * For this to be the case, there has to be quite large stable debt with an interest rate below the current variable rate.
                           * @param reserve The reserve state on which the user is getting rebalanced
                           * @param reserveCache The cached state of the reserve
                           * @param reserveAddress The address of the reserve
                           */
                          function validateRebalanceStableBorrowRate(
                            DataTypes.ReserveData storage reserve,
                            DataTypes.ReserveCache memory reserveCache,
                            address reserveAddress
                          ) internal view {
                            (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlags();
                            require(isActive, Errors.RESERVE_INACTIVE);
                            require(!isPaused, Errors.RESERVE_PAUSED);
                            uint256 totalDebt = IERC20(reserveCache.stableDebtTokenAddress).totalSupply() +
                              IERC20(reserveCache.variableDebtTokenAddress).totalSupply();
                            (uint256 liquidityRateVariableDebtOnly, , ) = IReserveInterestRateStrategy(
                              reserve.interestRateStrategyAddress
                            ).calculateInterestRates(
                                DataTypes.CalculateInterestRatesParams({
                                  unbacked: reserve.unbacked,
                                  liquidityAdded: 0,
                                  liquidityTaken: 0,
                                  totalStableDebt: 0,
                                  totalVariableDebt: totalDebt,
                                  averageStableBorrowRate: 0,
                                  reserveFactor: reserveCache.reserveFactor,
                                  reserve: reserveAddress,
                                  aToken: reserveCache.aTokenAddress
                                })
                              );
                            require(
                              reserveCache.currLiquidityRate <=
                                liquidityRateVariableDebtOnly.percentMul(REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD),
                              Errors.INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET
                            );
                          }
                          /**
                           * @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++) {
                              validateFlashloanSimple(reservesData[assets[i]]);
                            }
                          }
                          /**
                           * @notice Validates a flashloan action.
                           * @param reserve The state of the reserve
                           */
                          function validateFlashloanSimple(DataTypes.ReserveData storage reserve) 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);
                          }
                          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 params Additional parameters needed for the validation
                           */
                          function validateLiquidationCall(
                            DataTypes.UserConfigurationMap storage userConfig,
                            DataTypes.ReserveData storage collateralReserve,
                            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(
                              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.stableDebtTokenAddress).totalSupply() == 0, Errors.STABLE_DEBT_NOT_ZERO);
                            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 reservesData The state of all the reserves
                           * @param reservesList The addresses of all the active reserves
                           * @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(address => DataTypes.ReserveData) storage reservesData,
                            mapping(uint256 => address) storage reservesList,
                            mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
                            DataTypes.UserConfigurationMap memory userConfig,
                            uint256 reservesCount,
                            uint8 categoryId
                          ) internal view {
                            // category is invalid if the liq threshold is not set
                            require(
                              categoryId == 0 || eModeCategories[categoryId].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)) {
                                    DataTypes.ReserveConfigurationMap memory configuration = reservesData[reservesList[i]]
                                      .configuration;
                                    require(
                                      configuration.getEModeCategory() == categoryId,
                                      Errors.INCONSISTENT_EMODE_CATEGORY
                                    );
                                  }
                                }
                              }
                            }
                          }
                          /**
                           * @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.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: 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: MIT
                        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: 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 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;
                            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 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.12;
                        import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                        import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {IERC20Detailed} from '../../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                        import {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;
                          }
                        }
                        

                        File 8 of 15: InitializableImmutableAdminUpgradeabilityProxy
                        // 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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import './Proxy.sol';
                        import '../contracts/Address.sol';
                        /**
                         * @title BaseUpgradeabilityProxy
                         * @dev This contract implements a proxy that allows to change the
                         * implementation address to which it will delegate.
                         * Such a change is called an implementation upgrade.
                         */
                        contract BaseUpgradeabilityProxy is Proxy {
                          /**
                           * @dev Emitted when the implementation is upgraded.
                           * @param implementation Address of the new implementation.
                           */
                          event Upgraded(address indexed implementation);
                          /**
                           * @dev Storage slot with the address of the current implementation.
                           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                           * validated in the constructor.
                           */
                          bytes32 internal constant IMPLEMENTATION_SLOT =
                            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                          /**
                           * @dev Returns the current implementation.
                           * @return impl Address of the current implementation
                           */
                          function _implementation() internal 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.12;
                        import './BaseUpgradeabilityProxy.sol';
                        /**
                         * @title InitializableUpgradeabilityProxy
                         * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                         * implementation and init data.
                         */
                        contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                          /**
                           * @dev Contract initializer.
                           * @param _logic Address of the initial implementation.
                           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                           * It should include the signature and the parameters of the function to be called, as described in
                           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                           */
                          function initialize(address _logic, bytes memory _data) public payable {
                            require(_implementation() == address(0));
                            assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                            _setImplementation(_logic);
                            if (_data.length > 0) {
                              (bool success, ) = _logic.delegatecall(_data);
                              require(success);
                            }
                          }
                        }
                        // SPDX-License-Identifier: AGPL-3.0
                        pragma solidity 0.8.12;
                        /**
                         * @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.12;
                        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.12;
                        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 9 of 15: VariableDebtToken
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.8.12;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                          function _msgSender() internal view virtual returns (address payable) {
                            return 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.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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import {IERC20} from './IERC20.sol';
                        interface IERC20Detailed is IERC20 {
                          function name() external view returns (string memory);
                          function symbol() external view returns (string memory);
                          function decimals() external view returns (uint8);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                        pragma solidity 0.8.12;
                        /**
                         * @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: 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: 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);
                          /**
                           * @dev Emitted when pool receives ETH from liquidation
                           * @param sender Address of the caller
                           * @param amount Amount of the ETH
                           * @param permissionKey permission key that was used for the auction
                           */
                          event PoolReceivedETH(address sender, uint256 amount, bytes permissionKey);
                          /**
                           * @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://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 -> 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://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 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 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 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.12;
                        /**
                         * @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_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 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: MIT
                        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: 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 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;
                            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 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.12;
                        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.12;
                        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.12;
                        /**
                         * @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.12;
                        import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                        import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {IERC20Detailed} from '../../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                        import {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.12;
                        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.12;
                        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 10 of 15: InitializableImmutableAdminUpgradeabilityProxy
                        // 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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import './Proxy.sol';
                        import '../contracts/Address.sol';
                        /**
                         * @title BaseUpgradeabilityProxy
                         * @dev This contract implements a proxy that allows to change the
                         * implementation address to which it will delegate.
                         * Such a change is called an implementation upgrade.
                         */
                        contract BaseUpgradeabilityProxy is Proxy {
                          /**
                           * @dev Emitted when the implementation is upgraded.
                           * @param implementation Address of the new implementation.
                           */
                          event Upgraded(address indexed implementation);
                          /**
                           * @dev Storage slot with the address of the current implementation.
                           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                           * validated in the constructor.
                           */
                          bytes32 internal constant IMPLEMENTATION_SLOT =
                            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                          /**
                           * @dev Returns the current implementation.
                           * @return impl Address of the current implementation
                           */
                          function _implementation() internal 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.12;
                        import './BaseUpgradeabilityProxy.sol';
                        /**
                         * @title InitializableUpgradeabilityProxy
                         * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                         * implementation and init data.
                         */
                        contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                          /**
                           * @dev Contract initializer.
                           * @param _logic Address of the initial implementation.
                           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                           * It should include the signature and the parameters of the function to be called, as described in
                           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                           */
                          function initialize(address _logic, bytes memory _data) public payable {
                            require(_implementation() == address(0));
                            assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                            _setImplementation(_logic);
                            if (_data.length > 0) {
                              (bool success, ) = _logic.delegatecall(_data);
                              require(success);
                            }
                          }
                        }
                        // SPDX-License-Identifier: AGPL-3.0
                        pragma solidity 0.8.12;
                        /**
                         * @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.12;
                        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.12;
                        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 11 of 15: StableDebtTokenDisabled
                        // SPDX-License-Identifier: MIT
                        pragma solidity 0.8.12;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                          function _msgSender() internal view virtual returns (address payable) {
                            return 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.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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import {IERC20} from './IERC20.sol';
                        interface IERC20Detailed is IERC20 {
                          function name() external view returns (string memory);
                          function symbol() external view returns (string memory);
                          function decimals() external view returns (uint8);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                        pragma solidity 0.8.12;
                        /**
                         * @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: 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: 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);
                          /**
                           * @dev Emitted when pool receives ETH from liquidation
                           * @param sender Address of the caller
                           * @param amount Amount of the ETH
                           * @param permissionKey permission key that was used for the auction
                           */
                          event PoolReceivedETH(address sender, uint256 amount, bytes permissionKey);
                          /**
                           * @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://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 -> 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://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 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 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 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;
                        import {IInitializableDebtToken} from './IInitializableDebtToken.sol';
                        /**
                         * @title IStableDebtToken
                         * @author Aave
                         * @notice Defines the interface for the stable debt token
                         * @dev It does not inherit from IERC20 to save in code size
                         */
                        interface IStableDebtToken is IInitializableDebtToken {
                          /**
                           * @dev Emitted when new stable debt is minted
                           * @param user The address of the user who triggered the minting
                           * @param onBehalfOf The recipient of stable debt tokens
                           * @param amount The amount minted (user entered amount + balance increase from interest)
                           * @param currentBalance The balance of the user based on the previous balance and balance increase from interest
                           * @param balanceIncrease The increase in balance since the last action of the user 'onBehalfOf'
                           * @param newRate The rate of the debt after the minting
                           * @param avgStableRate The next average stable rate after the minting
                           * @param newTotalSupply The next total supply of the stable debt token after the action
                           */
                          event Mint(
                            address indexed user,
                            address indexed onBehalfOf,
                            uint256 amount,
                            uint256 currentBalance,
                            uint256 balanceIncrease,
                            uint256 newRate,
                            uint256 avgStableRate,
                            uint256 newTotalSupply
                          );
                          /**
                           * @dev Emitted when new stable debt is burned
                           * @param from The address from which the debt will be burned
                           * @param amount The amount being burned (user entered amount - balance increase from interest)
                           * @param currentBalance The balance of the user based on the previous balance and balance increase from interest
                           * @param balanceIncrease The increase in balance since the last action of 'from'
                           * @param avgStableRate The next average stable rate after the burning
                           * @param newTotalSupply The next total supply of the stable debt token after the action
                           */
                          event Burn(
                            address indexed from,
                            uint256 amount,
                            uint256 currentBalance,
                            uint256 balanceIncrease,
                            uint256 avgStableRate,
                            uint256 newTotalSupply
                          );
                          /**
                           * @notice Mints debt token to the `onBehalfOf` address.
                           * @dev The resulting rate is the weighted average between the rate of the new debt
                           * and the rate of the previous debt
                           * @param user The address receiving the borrowed underlying, being the delegatee in case
                           * of credit delegate, or same as `onBehalfOf` otherwise
                           * @param onBehalfOf The address receiving the debt tokens
                           * @param amount The amount of debt tokens to mint
                           * @param rate The rate of the debt being minted
                           * @return True if it is the first borrow, false otherwise
                           * @return The total stable debt
                           * @return The average stable borrow rate
                           */
                          function mint(
                            address user,
                            address onBehalfOf,
                            uint256 amount,
                            uint256 rate
                          ) external returns (bool, uint256, uint256);
                          /**
                           * @notice Burns debt of `user`
                           * @dev The resulting rate is the weighted average between the rate of the new debt
                           * and the rate of the previous debt
                           * @dev In some instances, a burn transaction will emit a mint event
                           * if the amount to burn is less than the interest the user earned
                           * @param from The address from which the debt will be burned
                           * @param amount The amount of debt tokens getting burned
                           * @return The total stable debt
                           * @return The average stable borrow rate
                           */
                          function burn(address from, uint256 amount) external returns (uint256, uint256);
                          /**
                           * @notice Returns the average rate of all the stable rate loans.
                           * @return The average stable rate
                           */
                          function getAverageStableRate() external view returns (uint256);
                          /**
                           * @notice Returns the stable rate of the user debt
                           * @param user The address of the user
                           * @return The stable rate of the user
                           */
                          function getUserStableRate(address user) external view returns (uint256);
                          /**
                           * @notice Returns the timestamp of the last update of the user
                           * @param user The address of the user
                           * @return The timestamp
                           */
                          function getUserLastUpdated(address user) external view returns (uint40);
                          /**
                           * @notice Returns the principal, the total supply, the average stable rate and the timestamp for the last update
                           * @return The principal
                           * @return The total supply
                           * @return The average stable rate
                           * @return The timestamp of the last update
                           */
                          function getSupplyData() external view returns (uint256, uint256, uint256, uint40);
                          /**
                           * @notice Returns the timestamp of the last update of the total supply
                           * @return The timestamp
                           */
                          function getTotalSupplyLastUpdated() external view returns (uint40);
                          /**
                           * @notice Returns the total supply and the average stable rate
                           * @return The total supply
                           * @return The average rate
                           */
                          function getTotalSupplyAndAvgRate() external view returns (uint256, uint256);
                          /**
                           * @notice Returns the principal debt balance of the user
                           * @return The debt balance of the user since the last burn/mint action
                           */
                          function principalBalanceOf(address user) external view returns (uint256);
                          /**
                           * @notice Returns the address of the underlying asset of this stableDebtToken (E.g. WETH for stableDebtWETH)
                           * @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.12;
                        /**
                         * @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_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 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;
                        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 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: MIT
                        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: 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 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;
                            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 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.12;
                        import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
                        import {MathUtils} from '../libraries/math/MathUtils.sol';
                        import {WadRayMath} from '../libraries/math/WadRayMath.sol';
                        import {Errors} from '../libraries/helpers/Errors.sol';
                        import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
                        import {IInitializableDebtToken} from '../../interfaces/IInitializableDebtToken.sol';
                        import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol';
                        import {IPool} from '../../interfaces/IPool.sol';
                        import {EIP712Base} from './base/EIP712Base.sol';
                        import {DebtTokenBase} from './base/DebtTokenBase.sol';
                        import {IncentivizedERC20} from './base/IncentivizedERC20.sol';
                        import {SafeCast} from '../../dependencies/openzeppelin/contracts/SafeCast.sol';
                        /**
                         * @title StableDebtToken
                         * @author Aave
                         * @notice Implements a stable debt token to track the borrowing positions of users
                         * at stable rate mode
                         * @dev Transfer and approve functionalities are disabled since its a non-transferable token
                         */
                        contract StableDebtToken is DebtTokenBase, IncentivizedERC20, IStableDebtToken {
                          using WadRayMath for uint256;
                          using SafeCast for uint256;
                          uint256 public constant DEBT_TOKEN_REVISION = 0x3;
                          // Map of users address and the timestamp of their last update (userAddress => lastUpdateTimestamp)
                          mapping(address => uint40) internal _timestamps;
                          uint128 internal _avgStableRate;
                          // Timestamp of the last update of the total supply
                          uint40 internal _totalSupplyTimestamp;
                          /**
                           * @dev Constructor.
                           * @param pool The address of the Pool contract
                           */
                          constructor(
                            IPool pool
                          ) DebtTokenBase() IncentivizedERC20(pool, 'STABLE_DEBT_TOKEN_IMPL', 'STABLE_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 IStableDebtToken
                          function getAverageStableRate() external view virtual override returns (uint256) {
                            return _avgStableRate;
                          }
                          /// @inheritdoc IStableDebtToken
                          function getUserLastUpdated(address user) external view virtual override returns (uint40) {
                            return _timestamps[user];
                          }
                          /// @inheritdoc IStableDebtToken
                          function getUserStableRate(address user) external view virtual override returns (uint256) {
                            return _userState[user].additionalData;
                          }
                          /// @inheritdoc IERC20
                          function balanceOf(address account) public view virtual override returns (uint256) {
                            uint256 accountBalance = super.balanceOf(account);
                            uint256 stableRate = _userState[account].additionalData;
                            if (accountBalance == 0) {
                              return 0;
                            }
                            uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest(
                              stableRate,
                              _timestamps[account]
                            );
                            return accountBalance.rayMul(cumulatedInterest);
                          }
                          /// @inheritdoc IStableDebtToken
                          /**
                           * @dev DEPRECATED, no stable debt should be minted in any operation
                           **/
                          function mint(
                            address,
                            address,
                            uint256,
                            uint256
                          ) external virtual override onlyPool returns (bool, uint256, uint256) {
                            revert('STABLE_BORROWING_DEPRECATED');
                          }
                          /// @inheritdoc IStableDebtToken
                          function burn(
                            address from,
                            uint256 amount
                          ) external virtual override onlyPool returns (uint256, uint256) {
                            (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(from);
                            uint256 previousSupply = totalSupply();
                            uint256 nextAvgStableRate = 0;
                            uint256 nextSupply = 0;
                            uint256 userStableRate = _userState[from].additionalData;
                            // Since the total supply and each single user debt accrue separately,
                            // there might be accumulation errors so that the last borrower repaying
                            // might actually try to repay more than the available debt supply.
                            // In this case we simply set the total supply and the avg stable rate to 0
                            if (previousSupply <= amount) {
                              _avgStableRate = 0;
                              _totalSupply = 0;
                            } else {
                              nextSupply = _totalSupply = previousSupply - amount;
                              uint256 firstTerm = uint256(_avgStableRate).rayMul(previousSupply.wadToRay());
                              uint256 secondTerm = userStableRate.rayMul(amount.wadToRay());
                              // For the same reason described above, when the last user is repaying it might
                              // happen that user rate * user balance > avg rate * total supply. In that case,
                              // we simply set the avg rate to 0
                              if (secondTerm >= firstTerm) {
                                nextAvgStableRate = _totalSupply = _avgStableRate = 0;
                              } else {
                                nextAvgStableRate = _avgStableRate = (
                                  (firstTerm - secondTerm).rayDiv(nextSupply.wadToRay())
                                ).toUint128();
                              }
                            }
                            if (amount == currentBalance) {
                              _userState[from].additionalData = 0;
                              _timestamps[from] = 0;
                            } else {
                              //solium-disable-next-line
                              _timestamps[from] = uint40(block.timestamp);
                            }
                            //solium-disable-next-line
                            _totalSupplyTimestamp = uint40(block.timestamp);
                            if (balanceIncrease > amount) {
                              uint256 amountToMint = balanceIncrease - amount;
                              _mint(from, amountToMint, previousSupply);
                              emit Transfer(address(0), from, amountToMint);
                              emit Mint(
                                from,
                                from,
                                amountToMint,
                                currentBalance,
                                balanceIncrease,
                                userStableRate,
                                nextAvgStableRate,
                                nextSupply
                              );
                            } else {
                              uint256 amountToBurn = amount - balanceIncrease;
                              _burn(from, amountToBurn, previousSupply);
                              emit Transfer(from, address(0), amountToBurn);
                              emit Burn(from, amountToBurn, currentBalance, balanceIncrease, nextAvgStableRate, nextSupply);
                            }
                            return (nextSupply, nextAvgStableRate);
                          }
                          /**
                           * @notice Calculates the increase in balance since the last user interaction
                           * @param user The address of the user for which the interest is being accumulated
                           * @return The previous principal balance
                           * @return The new principal balance
                           * @return The balance increase
                           */
                          function _calculateBalanceIncrease(
                            address user
                          ) internal view returns (uint256, uint256, uint256) {
                            uint256 previousPrincipalBalance = super.balanceOf(user);
                            if (previousPrincipalBalance == 0) {
                              return (0, 0, 0);
                            }
                            uint256 newPrincipalBalance = balanceOf(user);
                            return (
                              previousPrincipalBalance,
                              newPrincipalBalance,
                              newPrincipalBalance - previousPrincipalBalance
                            );
                          }
                          /// @inheritdoc IStableDebtToken
                          function getSupplyData() external view override returns (uint256, uint256, uint256, uint40) {
                            uint256 avgRate = _avgStableRate;
                            return (super.totalSupply(), _calcTotalSupply(avgRate), avgRate, _totalSupplyTimestamp);
                          }
                          /// @inheritdoc IStableDebtToken
                          function getTotalSupplyAndAvgRate() external view override returns (uint256, uint256) {
                            uint256 avgRate = _avgStableRate;
                            return (_calcTotalSupply(avgRate), avgRate);
                          }
                          /// @inheritdoc IERC20
                          function totalSupply() public view virtual override returns (uint256) {
                            return _calcTotalSupply(_avgStableRate);
                          }
                          /// @inheritdoc IStableDebtToken
                          function getTotalSupplyLastUpdated() external view override returns (uint40) {
                            return _totalSupplyTimestamp;
                          }
                          /// @inheritdoc IStableDebtToken
                          function principalBalanceOf(address user) external view virtual override returns (uint256) {
                            return super.balanceOf(user);
                          }
                          /// @inheritdoc IStableDebtToken
                          function UNDERLYING_ASSET_ADDRESS() external view override returns (address) {
                            return _underlyingAsset;
                          }
                          /**
                           * @notice Calculates the total supply
                           * @param avgRate The average rate at which the total supply increases
                           * @return The debt balance of the user since the last burn/mint action
                           */
                          function _calcTotalSupply(uint256 avgRate) internal view returns (uint256) {
                            uint256 principalSupply = super.totalSupply();
                            if (principalSupply == 0) {
                              return 0;
                            }
                            uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest(
                              avgRate,
                              _totalSupplyTimestamp
                            );
                            return principalSupply.rayMul(cumulatedInterest);
                          }
                          /**
                           * @notice Mints stable debt tokens to a user
                           * @param account The account receiving the debt tokens
                           * @param amount The amount being minted
                           * @param oldTotalSupply The total supply before the minting event
                           */
                          function _mint(address account, uint256 amount, uint256 oldTotalSupply) internal {
                            uint128 castAmount = amount.toUint128();
                            uint128 oldAccountBalance = _userState[account].balance;
                            _userState[account].balance = oldAccountBalance + castAmount;
                            if (address(_incentivesController) != address(0)) {
                              _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                            }
                          }
                          /**
                           * @notice Burns stable debt tokens of a user
                           * @param account The user getting his debt burned
                           * @param amount The amount being burned
                           * @param oldTotalSupply The total supply before the burning event
                           */
                          function _burn(address account, uint256 amount, uint256 oldTotalSupply) internal {
                            uint128 castAmount = amount.toUint128();
                            uint128 oldAccountBalance = _userState[account].balance;
                            _userState[account].balance = oldAccountBalance - castAmount;
                            if (address(_incentivesController) != address(0)) {
                              _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                            }
                          }
                          /// @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);
                          }
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        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.12;
                        /**
                         * @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.12;
                        import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                        import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {IERC20Detailed} from '../../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                        import {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 {IPool, StableDebtToken} from "@zerolendxyz/core-v3/contracts/protocol/tokenization/StableDebtToken.sol";
                        contract StableDebtTokenDisabled is StableDebtToken {
                            constructor(IPool pool) StableDebtToken(pool) {
                                // Intentionally left blank
                            }
                            function getRevision() internal pure virtual override returns (uint256) {
                                return 4;
                            }
                            function mint(
                                address,
                                address,
                                uint256,
                                uint256
                            ) external virtual override onlyPool returns (bool, uint256, uint256) {
                                revert("STABLE_BORROWING_DEPRECATED");
                            }
                        }
                        

                        File 12 of 15: AToken
                        // SPDX-License-Identifier: LGPL-3.0-or-later
                        pragma solidity 0.8.12;
                        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.12;
                        /*
                         * @dev Provides information about the current execution context, including the
                         * sender of the transaction and its data. While these are generally available
                         * via msg.sender and msg.data, they should not be accessed in such a direct
                         * manner, since when dealing with GSN meta-transactions the account sending and
                         * paying for execution may not be the actual sender (as far as an application
                         * is concerned).
                         *
                         * This contract is only required for intermediate, library-like contracts.
                         */
                        abstract contract Context {
                          function _msgSender() internal view virtual returns (address payable) {
                            return 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.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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import {IERC20} from './IERC20.sol';
                        interface IERC20Detailed is IERC20 {
                          function name() external view returns (string memory);
                          function symbol() external view returns (string memory);
                          function decimals() external view returns (uint8);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                        pragma solidity 0.8.12;
                        /**
                         * @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: 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: 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);
                          /**
                           * @dev Emitted when pool receives ETH from liquidation
                           * @param sender Address of the caller
                           * @param amount Amount of the ETH
                           * @param permissionKey permission key that was used for the auction
                           */
                          event PoolReceivedETH(address sender, uint256 amount, bytes permissionKey);
                          /**
                           * @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://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 -> 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://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 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 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 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.12;
                        /**
                         * @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_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 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: MIT
                        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: 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 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;
                            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 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.12;
                        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 = 0x2;
                          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);
                          }
                          function recall(address from, address to, uint256 amount) external onlyPoolAdmin {
                            _transfer(from, to, amount, true);
                          }
                        }
                        // SPDX-License-Identifier: BUSL-1.1
                        pragma solidity 0.8.12;
                        /**
                         * @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.12;
                        import {Context} from '../../../dependencies/openzeppelin/contracts/Context.sol';
                        import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {IERC20Detailed} from '../../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                        import {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.12;
                        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.12;
                        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 13 of 15: DefaultReserveInterestRateStrategy
                        // SPDX-License-Identifier: AGPL-3.0
                        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: AGPL-3.0
                        pragma solidity ^0.8.0;
                        import {IReserveInterestRateStrategy} from './IReserveInterestRateStrategy.sol';
                        import {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';
                        /**
                         * @title IDefaultInterestRateStrategy
                         * @author Aave
                         * @notice Defines the basic interface of the DefaultReserveInterestRateStrategy
                         */
                        interface IDefaultInterestRateStrategy is IReserveInterestRateStrategy {
                          /**
                           * @notice Returns the usage ratio at which the pool aims to obtain most competitive borrow rates.
                           * @return The optimal usage ratio, expressed in ray.
                           */
                          function OPTIMAL_USAGE_RATIO() external view returns (uint256);
                          /**
                           * @notice Returns the optimal stable to total debt ratio of the reserve.
                           * @return The optimal stable to total debt ratio, expressed in ray.
                           */
                          function OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO() external view returns (uint256);
                          /**
                           * @notice Returns the excess usage ratio above the optimal.
                           * @dev It's always equal to 1-optimal usage ratio (added as constant for gas optimizations)
                           * @return The max excess usage ratio, expressed in ray.
                           */
                          function MAX_EXCESS_USAGE_RATIO() external view returns (uint256);
                          /**
                           * @notice Returns the excess stable debt ratio above the optimal.
                           * @dev It's always equal to 1-optimal stable to total debt ratio (added as constant for gas optimizations)
                           * @return The max excess stable to total debt ratio, expressed in ray.
                           */
                          function MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO() external view returns (uint256);
                          /**
                           * @notice Returns the address of the PoolAddressesProvider
                           * @return The address of the PoolAddressesProvider contract
                           */
                          function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
                          /**
                           * @notice Returns the variable rate slope below optimal usage ratio
                           * @dev It's the variable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO
                           * @return The variable rate slope, expressed in ray
                           */
                          function getVariableRateSlope1() external view returns (uint256);
                          /**
                           * @notice Returns the variable rate slope above optimal usage ratio
                           * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO
                           * @return The variable rate slope, expressed in ray
                           */
                          function getVariableRateSlope2() external view returns (uint256);
                          /**
                           * @notice Returns the stable rate slope below optimal usage ratio
                           * @dev It's the stable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO
                           * @return The stable rate slope, expressed in ray
                           */
                          function getStableRateSlope1() external view returns (uint256);
                          /**
                           * @notice Returns the stable rate slope above optimal usage ratio
                           * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO
                           * @return The stable rate slope, expressed in ray
                           */
                          function getStableRateSlope2() external view returns (uint256);
                          /**
                           * @notice Returns the stable rate excess offset
                           * @dev It's an additional premium applied to the stable when stable debt > OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO
                           * @return The stable rate excess offset, expressed in ray
                           */
                          function getStableRateExcessOffset() external view returns (uint256);
                          /**
                           * @notice Returns the base stable borrow rate
                           * @return The base stable borrow rate, expressed in ray
                           */
                          function getBaseStableBorrowRate() external view returns (uint256);
                          /**
                           * @notice Returns the base variable borrow rate
                           * @return The base variable borrow rate, expressed in ray
                           */
                          function getBaseVariableBorrowRate() external view returns (uint256);
                          /**
                           * @notice Returns the maximum variable borrow rate
                           * @return The maximum variable borrow rate, expressed in ray
                           */
                          function getMaxVariableBorrowRate() external view returns (uint256);
                        }
                        // 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;
                        import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
                        /**
                         * @title IReserveInterestRateStrategy
                         * @author Aave
                         * @notice Interface for the calculation of the interest rates
                         */
                        interface IReserveInterestRateStrategy {
                          /**
                           * @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 rays
                           * @return stableBorrowRate The stable borrow rate expressed in rays
                           * @return variableBorrowRate The variable borrow rate expressed in rays
                           */
                          function calculateInterestRates(
                            DataTypes.CalculateInterestRatesParams memory params
                          ) external view returns (uint256, uint256, uint256);
                        }
                        // 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_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 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 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.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: MIT
                        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: 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 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;
                            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 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.0;
                        import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
                        import {WadRayMath} from '../libraries/math/WadRayMath.sol';
                        import {PercentageMath} from '../libraries/math/PercentageMath.sol';
                        import {DataTypes} from '../libraries/types/DataTypes.sol';
                        import {Errors} from '../libraries/helpers/Errors.sol';
                        import {IDefaultInterestRateStrategy} from '../../interfaces/IDefaultInterestRateStrategy.sol';
                        import {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol';
                        import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol';
                        /**
                         * @title DefaultReserveInterestRateStrategy contract
                         * @author Aave
                         * @notice Implements the calculation of the interest rates depending on the reserve state
                         * @dev The model of interest rate is based on 2 slopes, one before the `OPTIMAL_USAGE_RATIO`
                         * point of usage and another from that one to 100%.
                         * - An instance of this same contract, can't be used across different Aave markets, due to the caching
                         *   of the PoolAddressesProvider
                         */
                        contract DefaultReserveInterestRateStrategy is IDefaultInterestRateStrategy {
                          using WadRayMath for uint256;
                          using PercentageMath for uint256;
                          /// @inheritdoc IDefaultInterestRateStrategy
                          uint256 public immutable OPTIMAL_USAGE_RATIO;
                          /// @inheritdoc IDefaultInterestRateStrategy
                          uint256 public immutable OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO;
                          /// @inheritdoc IDefaultInterestRateStrategy
                          uint256 public immutable MAX_EXCESS_USAGE_RATIO;
                          /// @inheritdoc IDefaultInterestRateStrategy
                          uint256 public immutable MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO;
                          IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
                          // Base variable borrow rate when usage rate = 0. Expressed in ray
                          uint256 internal immutable _baseVariableBorrowRate;
                          // Slope of the variable interest curve when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO. Expressed in ray
                          uint256 internal immutable _variableRateSlope1;
                          // Slope of the variable interest curve when usage ratio > OPTIMAL_USAGE_RATIO. Expressed in ray
                          uint256 internal immutable _variableRateSlope2;
                          // Slope of the stable interest curve when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO. Expressed in ray
                          uint256 internal immutable _stableRateSlope1;
                          // Slope of the stable interest curve when usage ratio > OPTIMAL_USAGE_RATIO. Expressed in ray
                          uint256 internal immutable _stableRateSlope2;
                          // Premium on top of `_variableRateSlope1` for base stable borrowing rate
                          uint256 internal immutable _baseStableRateOffset;
                          // Additional premium applied to stable rate when stable debt surpass `OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO`
                          uint256 internal immutable _stableRateExcessOffset;
                          /**
                           * @dev Constructor.
                           * @param provider The address of the PoolAddressesProvider contract
                           * @param optimalUsageRatio The optimal usage ratio
                           * @param baseVariableBorrowRate The base variable borrow rate
                           * @param variableRateSlope1 The variable rate slope below optimal usage ratio
                           * @param variableRateSlope2 The variable rate slope above optimal usage ratio
                           * @param stableRateSlope1 The stable rate slope below optimal usage ratio
                           * @param stableRateSlope2 The stable rate slope above optimal usage ratio
                           * @param baseStableRateOffset The premium on top of variable rate for base stable borrowing rate
                           * @param stableRateExcessOffset The premium on top of stable rate when there stable debt surpass the threshold
                           * @param optimalStableToTotalDebtRatio The optimal stable debt to total debt ratio of the reserve
                           */
                          constructor(
                            IPoolAddressesProvider provider,
                            uint256 optimalUsageRatio,
                            uint256 baseVariableBorrowRate,
                            uint256 variableRateSlope1,
                            uint256 variableRateSlope2,
                            uint256 stableRateSlope1,
                            uint256 stableRateSlope2,
                            uint256 baseStableRateOffset,
                            uint256 stableRateExcessOffset,
                            uint256 optimalStableToTotalDebtRatio
                          ) {
                            require(WadRayMath.RAY >= optimalUsageRatio, Errors.INVALID_OPTIMAL_USAGE_RATIO);
                            require(
                              WadRayMath.RAY >= optimalStableToTotalDebtRatio,
                              Errors.INVALID_OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO
                            );
                            OPTIMAL_USAGE_RATIO = optimalUsageRatio;
                            MAX_EXCESS_USAGE_RATIO = WadRayMath.RAY - optimalUsageRatio;
                            OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO = optimalStableToTotalDebtRatio;
                            MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO = WadRayMath.RAY - optimalStableToTotalDebtRatio;
                            ADDRESSES_PROVIDER = provider;
                            _baseVariableBorrowRate = baseVariableBorrowRate;
                            _variableRateSlope1 = variableRateSlope1;
                            _variableRateSlope2 = variableRateSlope2;
                            _stableRateSlope1 = stableRateSlope1;
                            _stableRateSlope2 = stableRateSlope2;
                            _baseStableRateOffset = baseStableRateOffset;
                            _stableRateExcessOffset = stableRateExcessOffset;
                          }
                          /// @inheritdoc IDefaultInterestRateStrategy
                          function getVariableRateSlope1() external view returns (uint256) {
                            return _variableRateSlope1;
                          }
                          /// @inheritdoc IDefaultInterestRateStrategy
                          function getVariableRateSlope2() external view returns (uint256) {
                            return _variableRateSlope2;
                          }
                          /// @inheritdoc IDefaultInterestRateStrategy
                          function getStableRateSlope1() external view returns (uint256) {
                            return _stableRateSlope1;
                          }
                          /// @inheritdoc IDefaultInterestRateStrategy
                          function getStableRateSlope2() external view returns (uint256) {
                            return _stableRateSlope2;
                          }
                          /// @inheritdoc IDefaultInterestRateStrategy
                          function getStableRateExcessOffset() external view returns (uint256) {
                            return _stableRateExcessOffset;
                          }
                          /// @inheritdoc IDefaultInterestRateStrategy
                          function getBaseStableBorrowRate() public view returns (uint256) {
                            return _variableRateSlope1 + _baseStableRateOffset;
                          }
                          /// @inheritdoc IDefaultInterestRateStrategy
                          function getBaseVariableBorrowRate() external view override returns (uint256) {
                            return _baseVariableBorrowRate;
                          }
                          /// @inheritdoc IDefaultInterestRateStrategy
                          function getMaxVariableBorrowRate() external view override returns (uint256) {
                            return _baseVariableBorrowRate + _variableRateSlope1 + _variableRateSlope2;
                          }
                          struct CalcInterestRatesLocalVars {
                            uint256 availableLiquidity;
                            uint256 totalDebt;
                            uint256 currentVariableBorrowRate;
                            uint256 currentStableBorrowRate;
                            uint256 currentLiquidityRate;
                            uint256 borrowUsageRatio;
                            uint256 supplyUsageRatio;
                            uint256 stableToTotalDebtRatio;
                            uint256 availableLiquidityPlusDebt;
                          }
                          /// @inheritdoc IReserveInterestRateStrategy
                          function calculateInterestRates(
                            DataTypes.CalculateInterestRatesParams memory params
                          ) public view override returns (uint256, uint256, uint256) {
                            CalcInterestRatesLocalVars memory vars;
                            vars.totalDebt = params.totalStableDebt + params.totalVariableDebt;
                            vars.currentLiquidityRate = 0;
                            vars.currentVariableBorrowRate = _baseVariableBorrowRate;
                            vars.currentStableBorrowRate = getBaseStableBorrowRate();
                            if (vars.totalDebt != 0) {
                              vars.stableToTotalDebtRatio = params.totalStableDebt.rayDiv(vars.totalDebt);
                              vars.availableLiquidity =
                                IERC20(params.reserve).balanceOf(params.aToken) +
                                params.liquidityAdded -
                                params.liquidityTaken;
                              vars.availableLiquidityPlusDebt = vars.availableLiquidity + vars.totalDebt;
                              vars.borrowUsageRatio = vars.totalDebt.rayDiv(vars.availableLiquidityPlusDebt);
                              vars.supplyUsageRatio = vars.totalDebt.rayDiv(
                                vars.availableLiquidityPlusDebt + params.unbacked
                              );
                            }
                            if (vars.borrowUsageRatio > OPTIMAL_USAGE_RATIO) {
                              uint256 excessBorrowUsageRatio = (vars.borrowUsageRatio - OPTIMAL_USAGE_RATIO).rayDiv(
                                MAX_EXCESS_USAGE_RATIO
                              );
                              vars.currentStableBorrowRate +=
                                _stableRateSlope1 +
                                _stableRateSlope2.rayMul(excessBorrowUsageRatio);
                              vars.currentVariableBorrowRate +=
                                _variableRateSlope1 +
                                _variableRateSlope2.rayMul(excessBorrowUsageRatio);
                            } else {
                              vars.currentStableBorrowRate += _stableRateSlope1.rayMul(vars.borrowUsageRatio).rayDiv(
                                OPTIMAL_USAGE_RATIO
                              );
                              vars.currentVariableBorrowRate += _variableRateSlope1.rayMul(vars.borrowUsageRatio).rayDiv(
                                OPTIMAL_USAGE_RATIO
                              );
                            }
                            if (vars.stableToTotalDebtRatio > OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO) {
                              uint256 excessStableDebtRatio = (vars.stableToTotalDebtRatio -
                                OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO).rayDiv(MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO);
                              vars.currentStableBorrowRate += _stableRateExcessOffset.rayMul(excessStableDebtRatio);
                            }
                            vars.currentLiquidityRate = _getOverallBorrowRate(
                              params.totalStableDebt,
                              params.totalVariableDebt,
                              vars.currentVariableBorrowRate,
                              params.averageStableBorrowRate
                            ).rayMul(vars.supplyUsageRatio).percentMul(
                                PercentageMath.PERCENTAGE_FACTOR - params.reserveFactor
                              );
                            return (
                              vars.currentLiquidityRate,
                              vars.currentStableBorrowRate,
                              vars.currentVariableBorrowRate
                            );
                          }
                          /**
                           * @dev Calculates the overall borrow rate as the weighted average between the total variable debt and total stable
                           * debt
                           * @param totalStableDebt The total borrowed from the reserve at a stable rate
                           * @param totalVariableDebt The total borrowed from the reserve at a variable rate
                           * @param currentVariableBorrowRate The current variable borrow rate of the reserve
                           * @param currentAverageStableBorrowRate The current weighted average of all the stable rate loans
                           * @return The weighted averaged borrow rate
                           */
                          function _getOverallBorrowRate(
                            uint256 totalStableDebt,
                            uint256 totalVariableDebt,
                            uint256 currentVariableBorrowRate,
                            uint256 currentAverageStableBorrowRate
                          ) internal pure returns (uint256) {
                            uint256 totalDebt = totalStableDebt + totalVariableDebt;
                            if (totalDebt == 0) return 0;
                            uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate);
                            uint256 weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate);
                            uint256 overallBorrowRate = (weightedVariableRate + weightedStableRate).rayDiv(
                              totalDebt.wadToRay()
                            );
                            return overallBorrowRate;
                          }
                        }
                        

                        File 14 of 15: RewardsController
                        // SPDX-License-Identifier: AGPL-3.0
                        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: AGPL-3.0
                        pragma solidity 0.8.12;
                        import {IERC20} from './IERC20.sol';
                        interface IERC20Detailed is IERC20 {
                          function name() external view returns (string memory);
                          function symbol() external view returns (string memory);
                          function decimals() external view returns (uint8);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
                        pragma solidity 0.8.12;
                        /**
                         * @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.12;
                        /**
                         * @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.12;
                        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.12;
                        import {VersionedInitializable} from '@zerolendxyz/core-v3/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol';
                        import {SafeCast} from '@zerolendxyz/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol';
                        import {IScaledBalanceToken} from '@zerolendxyz/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.12;
                        import {IScaledBalanceToken} from '@zerolendxyz/core-v3/contracts/interfaces/IScaledBalanceToken.sol';
                        import {IERC20Detailed} from '@zerolendxyz/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol';
                        import {SafeCast} from '@zerolendxyz/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.12;
                        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.12;
                        /**
                         * @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.12;
                        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.12;
                        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;
                          }
                        }
                        

                        File 15 of 15: TellerWithMultiAssetSupport
                        // SPDX-License-Identifier: UNLICENSED
                        pragma solidity 0.8.21;
                        import {ERC20} from "@solmate/tokens/ERC20.sol";
                        import {WETH} from "@solmate/tokens/WETH.sol";
                        import {BoringVault} from "src/base/BoringVault.sol";
                        import {AccountantWithRateProviders} from "src/base/Roles/AccountantWithRateProviders.sol";
                        import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
                        import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
                        import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
                        import {Auth, Authority} from "@solmate/auth/Auth.sol";
                        import {ReentrancyGuard} from "@solmate/utils/ReentrancyGuard.sol";
                        import {IPausable} from "src/interfaces/IPausable.sol";
                        contract TellerWithMultiAssetSupport is Auth, BeforeTransferHook, ReentrancyGuard, IPausable {
                            using FixedPointMathLib for uint256;
                            using SafeTransferLib for ERC20;
                            using SafeTransferLib for WETH;
                            // ========================================= STRUCTS =========================================
                            /**
                             * @param allowDeposits bool indicating whether or not deposits are allowed for this asset.
                             * @param allowWithdraws bool indicating whether or not withdraws are allowed for this asset.
                             * @param sharePremium uint16 indicating the premium to apply to the shares minted.
                             *        where 40 represents a 40bps reduction in shares minted using this asset.
                             */
                            struct Asset {
                                bool allowDeposits;
                                bool allowWithdraws;
                                uint16 sharePremium;
                            }
                            // ========================================= CONSTANTS =========================================
                            /**
                             * @notice Native address used to tell the contract to handle native asset deposits.
                             */
                            address internal constant NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                            /**
                             * @notice The maximum possible share lock period.
                             */
                            uint256 internal constant MAX_SHARE_LOCK_PERIOD = 3 days;
                            /**
                             * @notice The maximum possible share premium that can be set using `updateAssetData`.
                             * @dev 1,000 or 10%
                             */
                            uint16 internal constant MAX_SHARE_PREMIUM = 1_000;
                            // ========================================= STATE =========================================
                            /**
                             * @notice Mapping ERC20s to their assetData.
                             */
                            mapping(ERC20 => Asset) public assetData;
                            /**
                             * @notice The deposit nonce used to map to a deposit hash.
                             */
                            uint96 public depositNonce = 1;
                            /**
                             * @notice After deposits, shares are locked to the msg.sender's address
                             *         for `shareLockPeriod`.
                             * @dev During this time all trasnfers from msg.sender will revert, and
                             *      deposits are refundable.
                             */
                            uint64 public shareLockPeriod;
                            /**
                             * @notice Used to pause calls to `deposit` and `depositWithPermit`.
                             */
                            bool public isPaused;
                            /**
                             * @dev Maps deposit nonce to keccak256(address receiver, address depositAsset, uint256 depositAmount, uint256 shareAmount, uint256 timestamp, uint256 shareLockPeriod).
                             */
                            mapping(uint256 => bytes32) public publicDepositHistory;
                            /**
                             * @notice Maps user address to the time their shares will be unlocked.
                             */
                            mapping(address => uint256) public shareUnlockTime;
                            /**
                             * @notice Mapping `from` address to a bool to deny them from transferring shares.
                             */
                            mapping(address => bool) public fromDenyList;
                            /**
                             * @notice Mapping `to` address to a bool to deny them from receiving shares.
                             */
                            mapping(address => bool) public toDenyList;
                            /**
                             * @notice Mapping `opeartor` address to a bool to deny them from calling `transfer` or `transferFrom`.
                             */
                            mapping(address => bool) public operatorDenyList;
                            //============================== ERRORS ===============================
                            error TellerWithMultiAssetSupport__ShareLockPeriodTooLong();
                            error TellerWithMultiAssetSupport__SharesAreLocked();
                            error TellerWithMultiAssetSupport__SharesAreUnLocked();
                            error TellerWithMultiAssetSupport__BadDepositHash();
                            error TellerWithMultiAssetSupport__AssetNotSupported();
                            error TellerWithMultiAssetSupport__ZeroAssets();
                            error TellerWithMultiAssetSupport__MinimumMintNotMet();
                            error TellerWithMultiAssetSupport__MinimumAssetsNotMet();
                            error TellerWithMultiAssetSupport__PermitFailedAndAllowanceTooLow();
                            error TellerWithMultiAssetSupport__ZeroShares();
                            error TellerWithMultiAssetSupport__DualDeposit();
                            error TellerWithMultiAssetSupport__Paused();
                            error TellerWithMultiAssetSupport__TransferDenied(address from, address to, address operator);
                            error TellerWithMultiAssetSupport__SharePremiumTooLarge();
                            //============================== EVENTS ===============================
                            event Paused();
                            event Unpaused();
                            event AssetDataUpdated(address indexed asset, bool allowDeposits, bool allowWithdraws, uint16 sharePremium);
                            event Deposit(
                                uint256 indexed nonce,
                                address indexed receiver,
                                address indexed depositAsset,
                                uint256 depositAmount,
                                uint256 shareAmount,
                                uint256 depositTimestamp,
                                uint256 shareLockPeriodAtTimeOfDeposit
                            );
                            event BulkDeposit(address indexed asset, uint256 depositAmount);
                            event BulkWithdraw(address indexed asset, uint256 shareAmount);
                            event DepositRefunded(uint256 indexed nonce, bytes32 depositHash, address indexed user);
                            event DenyFrom(address indexed user);
                            event DenyTo(address indexed user);
                            event DenyOperator(address indexed user);
                            event AllowFrom(address indexed user);
                            event AllowTo(address indexed user);
                            event AllowOperator(address indexed user);
                            //============================== IMMUTABLES ===============================
                            /**
                             * @notice The BoringVault this contract is working with.
                             */
                            BoringVault public immutable vault;
                            /**
                             * @notice The AccountantWithRateProviders this contract is working with.
                             */
                            AccountantWithRateProviders public immutable accountant;
                            /**
                             * @notice One share of the BoringVault.
                             */
                            uint256 internal immutable ONE_SHARE;
                            /**
                             * @notice The native wrapper contract.
                             */
                            WETH public immutable nativeWrapper;
                            constructor(address _owner, address _vault, address _accountant, address _weth)
                                Auth(_owner, Authority(address(0)))
                            {
                                vault = BoringVault(payable(_vault));
                                ONE_SHARE = 10 ** vault.decimals();
                                accountant = AccountantWithRateProviders(_accountant);
                                nativeWrapper = WETH(payable(_weth));
                            }
                            // ========================================= ADMIN FUNCTIONS =========================================
                            /**
                             * @notice Pause this contract, which prevents future calls to `deposit` and `depositWithPermit`.
                             * @dev Callable by MULTISIG_ROLE.
                             */
                            function pause() external requiresAuth {
                                isPaused = true;
                                emit Paused();
                            }
                            /**
                             * @notice Unpause this contract, which allows future calls to `deposit` and `depositWithPermit`.
                             * @dev Callable by MULTISIG_ROLE.
                             */
                            function unpause() external requiresAuth {
                                isPaused = false;
                                emit Unpaused();
                            }
                            /**
                             * @notice Updates the asset data for a given asset.
                             * @dev The accountant must also support pricing this asset, else the `deposit` call will revert.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function updateAssetData(ERC20 asset, bool allowDeposits, bool allowWithdraws, uint16 sharePremium)
                                external
                                requiresAuth
                            {
                                if (sharePremium > MAX_SHARE_PREMIUM) revert TellerWithMultiAssetSupport__SharePremiumTooLarge();
                                assetData[asset] = Asset(allowDeposits, allowWithdraws, sharePremium);
                                emit AssetDataUpdated(address(asset), allowDeposits, allowWithdraws, sharePremium);
                            }
                            /**
                             * @notice Sets the share lock period.
                             * @dev This not only locks shares to the user address, but also serves as the pending deposit period, where deposits can be reverted.
                             * @dev If a new shorter share lock period is set, users with pending share locks could make a new deposit to receive 1 wei shares,
                             *      and have their shares unlock sooner than their original deposit allows. This state would allow for the user deposit to be refunded,
                             *      but only if they have not transferred their shares out of there wallet. This is an accepted limitation, and should be known when decreasing
                             *      the share lock period.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function setShareLockPeriod(uint64 _shareLockPeriod) external requiresAuth {
                                if (_shareLockPeriod > MAX_SHARE_LOCK_PERIOD) revert TellerWithMultiAssetSupport__ShareLockPeriodTooLong();
                                shareLockPeriod = _shareLockPeriod;
                            }
                            /**
                             * @notice Deny a user from transferring or receiving shares.
                             * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
                             */
                            function denyAll(address user) external requiresAuth {
                                fromDenyList[user] = true;
                                toDenyList[user] = true;
                                operatorDenyList[user] = true;
                                emit DenyFrom(user);
                                emit DenyTo(user);
                                emit DenyOperator(user);
                            }
                            /**
                             * @notice Allow a user to transfer or receive shares.
                             * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
                             */
                            function allowAll(address user) external requiresAuth {
                                fromDenyList[user] = false;
                                toDenyList[user] = false;
                                operatorDenyList[user] = false;
                                emit AllowFrom(user);
                                emit AllowTo(user);
                                emit AllowOperator(user);
                            }
                            /**
                             * @notice Deny a user from transferring shares.
                             * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
                             */
                            function denyFrom(address user) external requiresAuth {
                                fromDenyList[user] = true;
                                emit DenyFrom(user);
                            }
                            /**
                             * @notice Allow a user to transfer shares.
                             * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
                             */
                            function allowFrom(address user) external requiresAuth {
                                fromDenyList[user] = false;
                                emit AllowFrom(user);
                            }
                            /**
                             * @notice Deny a user from receiving shares.
                             * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
                             */
                            function denyTo(address user) external requiresAuth {
                                toDenyList[user] = true;
                                emit DenyTo(user);
                            }
                            /**
                             * @notice Allow a user to receive shares.
                             * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
                             */
                            function allowTo(address user) external requiresAuth {
                                toDenyList[user] = false;
                                emit AllowTo(user);
                            }
                            /**
                             * @notice Deny an operator from transferring shares.
                             * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
                             */
                            function denyOperator(address user) external requiresAuth {
                                operatorDenyList[user] = true;
                                emit DenyOperator(user);
                            }
                            /**
                             * @notice Allow an operator to transfer shares.
                             * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
                             */
                            function allowOperator(address user) external requiresAuth {
                                operatorDenyList[user] = false;
                                emit AllowOperator(user);
                            }
                            // ========================================= BeforeTransferHook FUNCTIONS =========================================
                            /**
                             * @notice Implement beforeTransfer hook to check if shares are locked, or if `from`, `to`, or `operator` are on the deny list.
                             * @notice If share lock period is set to zero, then users will be able to mint and transfer in the same tx.
                             *         if this behavior is not desired then a share lock period of >=1 should be used.
                             */
                            function beforeTransfer(address from, address to, address operator) public view virtual {
                                if (fromDenyList[from] || toDenyList[to] || operatorDenyList[operator]) {
                                    revert TellerWithMultiAssetSupport__TransferDenied(from, to, operator);
                                }
                                if (shareUnlockTime[from] > block.timestamp) revert TellerWithMultiAssetSupport__SharesAreLocked();
                            }
                            // ========================================= REVERT DEPOSIT FUNCTIONS =========================================
                            /**
                             * @notice Allows DEPOSIT_REFUNDER_ROLE to revert a pending deposit.
                             * @dev Once a deposit share lock period has passed, it can no longer be reverted.
                             * @dev It is possible the admin does not setup the BoringVault to call the transfer hook,
                             *      but this contract can still be saving share lock state. In the event this happens
                             *      deposits are still refundable if the user has not transferred their shares.
                             *      But there is no guarantee that the user has not transferred their shares.
                             * @dev Callable by STRATEGIST_MULTISIG_ROLE.
                             */
                            function refundDeposit(
                                uint256 nonce,
                                address receiver,
                                address depositAsset,
                                uint256 depositAmount,
                                uint256 shareAmount,
                                uint256 depositTimestamp,
                                uint256 shareLockUpPeriodAtTimeOfDeposit
                            ) external requiresAuth {
                                if ((block.timestamp - depositTimestamp) >= shareLockUpPeriodAtTimeOfDeposit) {
                                    // Shares are already unlocked, so we can not revert deposit.
                                    revert TellerWithMultiAssetSupport__SharesAreUnLocked();
                                }
                                bytes32 depositHash = keccak256(
                                    abi.encode(
                                        receiver, depositAsset, depositAmount, shareAmount, depositTimestamp, shareLockUpPeriodAtTimeOfDeposit
                                    )
                                );
                                if (publicDepositHistory[nonce] != depositHash) revert TellerWithMultiAssetSupport__BadDepositHash();
                                // Delete hash to prevent refund gas.
                                delete publicDepositHistory[nonce];
                                // If deposit used native asset, send user back wrapped native asset.
                                depositAsset = depositAsset == NATIVE ? address(nativeWrapper) : depositAsset;
                                // Burn shares and refund assets to receiver.
                                vault.exit(receiver, ERC20(depositAsset), depositAmount, receiver, shareAmount);
                                emit DepositRefunded(nonce, depositHash, receiver);
                            }
                            // ========================================= USER FUNCTIONS =========================================
                            /**
                             * @notice Allows users to deposit into the BoringVault, if this contract is not paused.
                             * @dev Publicly callable.
                             */
                            function deposit(ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint)
                                public
                                payable
                                requiresAuth
                                nonReentrant
                                returns (uint256 shares)
                            {
                                if (isPaused) revert TellerWithMultiAssetSupport__Paused();
                                Asset memory asset = assetData[depositAsset];
                                if (!asset.allowDeposits) revert TellerWithMultiAssetSupport__AssetNotSupported();
                                if (address(depositAsset) == NATIVE) {
                                    if (msg.value == 0) revert TellerWithMultiAssetSupport__ZeroAssets();
                                    nativeWrapper.deposit{value: msg.value}();
                                    depositAmount = msg.value;
                                    shares = depositAmount.mulDivDown(ONE_SHARE, accountant.getRateInQuoteSafe(nativeWrapper));
                                    shares = asset.sharePremium > 0 ? shares.mulDivDown(1e4 - asset.sharePremium, 1e4) : shares;
                                    if (shares < minimumMint) revert TellerWithMultiAssetSupport__MinimumMintNotMet();
                                    // `from` is address(this) since user already sent value.
                                    nativeWrapper.safeApprove(address(vault), depositAmount);
                                    vault.enter(address(this), nativeWrapper, depositAmount, msg.sender, shares);
                                } else {
                                    if (msg.value > 0) revert TellerWithMultiAssetSupport__DualDeposit();
                                    shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, msg.sender, asset);
                                }
                                _afterPublicDeposit(msg.sender, depositAsset, depositAmount, shares, shareLockPeriod);
                            }
                            /**
                             * @notice Allows users to deposit into BoringVault using permit.
                             * @dev Publicly callable.
                             */
                            function depositWithPermit(
                                ERC20 depositAsset,
                                uint256 depositAmount,
                                uint256 minimumMint,
                                uint256 deadline,
                                uint8 v,
                                bytes32 r,
                                bytes32 s
                            ) public requiresAuth nonReentrant returns (uint256 shares) {
                                if (isPaused) revert TellerWithMultiAssetSupport__Paused();
                                Asset memory asset = assetData[depositAsset];
                                if (!asset.allowDeposits) revert TellerWithMultiAssetSupport__AssetNotSupported();
                                try depositAsset.permit(msg.sender, address(vault), depositAmount, deadline, v, r, s) {}
                                catch {
                                    if (depositAsset.allowance(msg.sender, address(vault)) < depositAmount) {
                                        revert TellerWithMultiAssetSupport__PermitFailedAndAllowanceTooLow();
                                    }
                                }
                                shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, msg.sender, asset);
                                _afterPublicDeposit(msg.sender, depositAsset, depositAmount, shares, shareLockPeriod);
                            }
                            /**
                             * @notice Allows on ramp role to deposit into this contract.
                             * @dev Does NOT support native deposits.
                             * @dev Callable by SOLVER_ROLE.
                             */
                            function bulkDeposit(ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint, address to)
                                external
                                requiresAuth
                                nonReentrant
                                returns (uint256 shares)
                            {
                                if (isPaused) revert TellerWithMultiAssetSupport__Paused();
                                Asset memory asset = assetData[depositAsset];
                                if (!asset.allowDeposits) revert TellerWithMultiAssetSupport__AssetNotSupported();
                                shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, to, asset);
                                emit BulkDeposit(address(depositAsset), depositAmount);
                            }
                            /**
                             * @notice Allows off ramp role to withdraw from this contract.
                             * @dev Callable by SOLVER_ROLE.
                             */
                            function bulkWithdraw(ERC20 withdrawAsset, uint256 shareAmount, uint256 minimumAssets, address to)
                                external
                                requiresAuth
                                returns (uint256 assetsOut)
                            {
                                if (isPaused) revert TellerWithMultiAssetSupport__Paused();
                                Asset memory asset = assetData[withdrawAsset];
                                if (!asset.allowWithdraws) revert TellerWithMultiAssetSupport__AssetNotSupported();
                                if (shareAmount == 0) revert TellerWithMultiAssetSupport__ZeroShares();
                                assetsOut = shareAmount.mulDivDown(accountant.getRateInQuoteSafe(withdrawAsset), ONE_SHARE);
                                if (assetsOut < minimumAssets) revert TellerWithMultiAssetSupport__MinimumAssetsNotMet();
                                vault.exit(to, withdrawAsset, assetsOut, msg.sender, shareAmount);
                                emit BulkWithdraw(address(withdrawAsset), shareAmount);
                            }
                            // ========================================= INTERNAL HELPER FUNCTIONS =========================================
                            /**
                             * @notice Implements a common ERC20 deposit into BoringVault.
                             */
                            function _erc20Deposit(
                                ERC20 depositAsset,
                                uint256 depositAmount,
                                uint256 minimumMint,
                                address to,
                                Asset memory asset
                            ) internal returns (uint256 shares) {
                                if (depositAmount == 0) revert TellerWithMultiAssetSupport__ZeroAssets();
                                shares = depositAmount.mulDivDown(ONE_SHARE, accountant.getRateInQuoteSafe(depositAsset));
                                shares = asset.sharePremium > 0 ? shares.mulDivDown(1e4 - asset.sharePremium, 1e4) : shares;
                                if (shares < minimumMint) revert TellerWithMultiAssetSupport__MinimumMintNotMet();
                                vault.enter(msg.sender, depositAsset, depositAmount, to, shares);
                            }
                            /**
                             * @notice Handle share lock logic, and event.
                             */
                            function _afterPublicDeposit(
                                address user,
                                ERC20 depositAsset,
                                uint256 depositAmount,
                                uint256 shares,
                                uint256 currentShareLockPeriod
                            ) internal {
                                shareUnlockTime[user] = block.timestamp + currentShareLockPeriod;
                                uint256 nonce = depositNonce;
                                publicDepositHistory[nonce] =
                                    keccak256(abi.encode(user, depositAsset, depositAmount, shares, block.timestamp, currentShareLockPeriod));
                                depositNonce++;
                                emit Deposit(nonce, user, address(depositAsset), depositAmount, shares, block.timestamp, currentShareLockPeriod);
                            }
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
                        /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
                        /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
                        abstract contract ERC20 {
                            /*//////////////////////////////////////////////////////////////
                                                         EVENTS
                            //////////////////////////////////////////////////////////////*/
                            event Transfer(address indexed from, address indexed to, uint256 amount);
                            event Approval(address indexed owner, address indexed spender, uint256 amount);
                            /*//////////////////////////////////////////////////////////////
                                                    METADATA STORAGE
                            //////////////////////////////////////////////////////////////*/
                            string public name;
                            string public symbol;
                            uint8 public immutable decimals;
                            /*//////////////////////////////////////////////////////////////
                                                      ERC20 STORAGE
                            //////////////////////////////////////////////////////////////*/
                            uint256 public totalSupply;
                            mapping(address => uint256) public balanceOf;
                            mapping(address => mapping(address => uint256)) public allowance;
                            /*//////////////////////////////////////////////////////////////
                                                    EIP-2612 STORAGE
                            //////////////////////////////////////////////////////////////*/
                            uint256 internal immutable INITIAL_CHAIN_ID;
                            bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
                            mapping(address => uint256) public nonces;
                            /*//////////////////////////////////////////////////////////////
                                                       CONSTRUCTOR
                            //////////////////////////////////////////////////////////////*/
                            constructor(
                                string memory _name,
                                string memory _symbol,
                                uint8 _decimals
                            ) {
                                name = _name;
                                symbol = _symbol;
                                decimals = _decimals;
                                INITIAL_CHAIN_ID = block.chainid;
                                INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
                            }
                            /*//////////////////////////////////////////////////////////////
                                                       ERC20 LOGIC
                            //////////////////////////////////////////////////////////////*/
                            function approve(address spender, uint256 amount) public virtual returns (bool) {
                                allowance[msg.sender][spender] = amount;
                                emit Approval(msg.sender, spender, amount);
                                return true;
                            }
                            function transfer(address to, uint256 amount) public virtual returns (bool) {
                                balanceOf[msg.sender] -= amount;
                                // Cannot overflow because the sum of all user
                                // balances can't exceed the max uint256 value.
                                unchecked {
                                    balanceOf[to] += amount;
                                }
                                emit Transfer(msg.sender, to, amount);
                                return true;
                            }
                            function transferFrom(
                                address from,
                                address to,
                                uint256 amount
                            ) public virtual returns (bool) {
                                uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
                                if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
                                balanceOf[from] -= amount;
                                // Cannot overflow because the sum of all user
                                // balances can't exceed the max uint256 value.
                                unchecked {
                                    balanceOf[to] += amount;
                                }
                                emit Transfer(from, to, amount);
                                return true;
                            }
                            /*//////////////////////////////////////////////////////////////
                                                     EIP-2612 LOGIC
                            //////////////////////////////////////////////////////////////*/
                            function permit(
                                address owner,
                                address spender,
                                uint256 value,
                                uint256 deadline,
                                uint8 v,
                                bytes32 r,
                                bytes32 s
                            ) public virtual {
                                require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
                                // Unchecked because the only math done is incrementing
                                // the owner's nonce which cannot realistically overflow.
                                unchecked {
                                    address recoveredAddress = ecrecover(
                                        keccak256(
                                            abi.encodePacked(
                                                "\\x19\\x01",
                                                DOMAIN_SEPARATOR(),
                                                keccak256(
                                                    abi.encode(
                                                        keccak256(
                                                            "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                                        ),
                                                        owner,
                                                        spender,
                                                        value,
                                                        nonces[owner]++,
                                                        deadline
                                                    )
                                                )
                                            )
                                        ),
                                        v,
                                        r,
                                        s
                                    );
                                    require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                                    allowance[recoveredAddress][spender] = value;
                                }
                                emit Approval(owner, spender, value);
                            }
                            function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                                return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
                            }
                            function computeDomainSeparator() internal view virtual returns (bytes32) {
                                return
                                    keccak256(
                                        abi.encode(
                                            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                                            keccak256(bytes(name)),
                                            keccak256("1"),
                                            block.chainid,
                                            address(this)
                                        )
                                    );
                            }
                            /*//////////////////////////////////////////////////////////////
                                                INTERNAL MINT/BURN LOGIC
                            //////////////////////////////////////////////////////////////*/
                            function _mint(address to, uint256 amount) internal virtual {
                                totalSupply += amount;
                                // Cannot overflow because the sum of all user
                                // balances can't exceed the max uint256 value.
                                unchecked {
                                    balanceOf[to] += amount;
                                }
                                emit Transfer(address(0), to, amount);
                            }
                            function _burn(address from, uint256 amount) internal virtual {
                                balanceOf[from] -= amount;
                                // Cannot underflow because a user's balance
                                // will never be larger than the total supply.
                                unchecked {
                                    totalSupply -= amount;
                                }
                                emit Transfer(from, address(0), amount);
                            }
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        import {ERC20} from "./ERC20.sol";
                        import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
                        /// @notice Minimalist and modern Wrapped Ether implementation.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)
                        /// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
                        contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
                            using SafeTransferLib for address;
                            event Deposit(address indexed from, uint256 amount);
                            event Withdrawal(address indexed to, uint256 amount);
                            function deposit() public payable virtual {
                                _mint(msg.sender, msg.value);
                                emit Deposit(msg.sender, msg.value);
                            }
                            function withdraw(uint256 amount) public virtual {
                                _burn(msg.sender, amount);
                                emit Withdrawal(msg.sender, amount);
                                msg.sender.safeTransferETH(amount);
                            }
                            receive() external payable virtual {
                                deposit();
                            }
                        }
                        // SPDX-License-Identifier: UNLICENSED
                        pragma solidity 0.8.21;
                        import {Address} from "@openzeppelin/contracts/utils/Address.sol";
                        import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
                        import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
                        import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
                        import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
                        import {ERC20} from "@solmate/tokens/ERC20.sol";
                        import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
                        import {Auth, Authority} from "@solmate/auth/Auth.sol";
                        contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
                            using Address for address;
                            using SafeTransferLib for ERC20;
                            using FixedPointMathLib for uint256;
                            // ========================================= STATE =========================================
                            /**
                             * @notice Contract responsbile for implementing `beforeTransfer`.
                             */
                            BeforeTransferHook public hook;
                            //============================== EVENTS ===============================
                            event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
                            event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);
                            //============================== CONSTRUCTOR ===============================
                            constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
                                ERC20(_name, _symbol, _decimals)
                                Auth(_owner, Authority(address(0)))
                            {}
                            //============================== MANAGE ===============================
                            /**
                             * @notice Allows manager to make an arbitrary function call from this contract.
                             * @dev Callable by MANAGER_ROLE.
                             */
                            function manage(address target, bytes calldata data, uint256 value)
                                external
                                requiresAuth
                                returns (bytes memory result)
                            {
                                result = target.functionCallWithValue(data, value);
                            }
                            /**
                             * @notice Allows manager to make arbitrary function calls from this contract.
                             * @dev Callable by MANAGER_ROLE.
                             */
                            function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
                                external
                                requiresAuth
                                returns (bytes[] memory results)
                            {
                                uint256 targetsLength = targets.length;
                                results = new bytes[](targetsLength);
                                for (uint256 i; i < targetsLength; ++i) {
                                    results[i] = targets[i].functionCallWithValue(data[i], values[i]);
                                }
                            }
                            //============================== ENTER ===============================
                            /**
                             * @notice Allows minter to mint shares, in exchange for assets.
                             * @dev If assetAmount is zero, no assets are transferred in.
                             * @dev Callable by MINTER_ROLE.
                             */
                            function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
                                external
                                requiresAuth
                            {
                                // Transfer assets in
                                if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);
                                // Mint shares.
                                _mint(to, shareAmount);
                                emit Enter(from, address(asset), assetAmount, to, shareAmount);
                            }
                            //============================== EXIT ===============================
                            /**
                             * @notice Allows burner to burn shares, in exchange for assets.
                             * @dev If assetAmount is zero, no assets are transferred out.
                             * @dev Callable by BURNER_ROLE.
                             */
                            function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
                                external
                                requiresAuth
                            {
                                // Burn shares.
                                _burn(from, shareAmount);
                                // Transfer assets out.
                                if (assetAmount > 0) asset.safeTransfer(to, assetAmount);
                                emit Exit(to, address(asset), assetAmount, from, shareAmount);
                            }
                            //============================== BEFORE TRANSFER HOOK ===============================
                            /**
                             * @notice Sets the share locker.
                             * @notice If set to zero address, the share locker logic is disabled.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function setBeforeTransferHook(address _hook) external requiresAuth {
                                hook = BeforeTransferHook(_hook);
                            }
                            /**
                             * @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
                             */
                            function _callBeforeTransfer(address from, address to) internal view {
                                if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
                            }
                            function transfer(address to, uint256 amount) public override returns (bool) {
                                _callBeforeTransfer(msg.sender, to);
                                return super.transfer(to, amount);
                            }
                            function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
                                _callBeforeTransfer(from, to);
                                return super.transferFrom(from, to, amount);
                            }
                            //============================== RECEIVE ===============================
                            receive() external payable {}
                        }
                        // SPDX-License-Identifier: UNLICENSED
                        pragma solidity 0.8.21;
                        import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
                        import {IRateProvider} from "src/interfaces/IRateProvider.sol";
                        import {ERC20} from "@solmate/tokens/ERC20.sol";
                        import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
                        import {BoringVault} from "src/base/BoringVault.sol";
                        import {Auth, Authority} from "@solmate/auth/Auth.sol";
                        import {IPausable} from "src/interfaces/IPausable.sol";
                        contract AccountantWithRateProviders is Auth, IRateProvider, IPausable {
                            using FixedPointMathLib for uint256;
                            using SafeTransferLib for ERC20;
                            // ========================================= STRUCTS =========================================
                            /**
                             * @param payoutAddress the address `claimFees` sends fees to
                             * @param highwaterMark the highest value of the BoringVault's share price
                             * @param feesOwedInBase total pending fees owed in terms of base
                             * @param totalSharesLastUpdate total amount of shares the last exchange rate update
                             * @param exchangeRate the current exchange rate in terms of base
                             * @param allowedExchangeRateChangeUpper the max allowed change to exchange rate from an update
                             * @param allowedExchangeRateChangeLower the min allowed change to exchange rate from an update
                             * @param lastUpdateTimestamp the block timestamp of the last exchange rate update
                             * @param isPaused whether or not this contract is paused
                             * @param minimumUpdateDelayInSeconds the minimum amount of time that must pass between
                             *        exchange rate updates, such that the update won't trigger the contract to be paused
                             * @param managementFee the management fee
                             * @param performanceFee the performance fee
                             */
                            struct AccountantState {
                                address payoutAddress;
                                uint96 highwaterMark;
                                uint128 feesOwedInBase;
                                uint128 totalSharesLastUpdate;
                                uint96 exchangeRate;
                                uint16 allowedExchangeRateChangeUpper;
                                uint16 allowedExchangeRateChangeLower;
                                uint64 lastUpdateTimestamp;
                                bool isPaused;
                                uint24 minimumUpdateDelayInSeconds;
                                uint16 managementFee;
                                uint16 performanceFee;
                            }
                            /**
                             * @param isPeggedToBase whether or not the asset is 1:1 with the base asset
                             * @param rateProvider the rate provider for this asset if `isPeggedToBase` is false
                             */
                            struct RateProviderData {
                                bool isPeggedToBase;
                                IRateProvider rateProvider;
                            }
                            // ========================================= STATE =========================================
                            /**
                             * @notice Store the accountant state in 3 packed slots.
                             */
                            AccountantState public accountantState;
                            /**
                             * @notice Maps ERC20s to their RateProviderData.
                             */
                            mapping(ERC20 => RateProviderData) public rateProviderData;
                            //============================== ERRORS ===============================
                            error AccountantWithRateProviders__UpperBoundTooSmall();
                            error AccountantWithRateProviders__LowerBoundTooLarge();
                            error AccountantWithRateProviders__ManagementFeeTooLarge();
                            error AccountantWithRateProviders__PerformanceFeeTooLarge();
                            error AccountantWithRateProviders__Paused();
                            error AccountantWithRateProviders__ZeroFeesOwed();
                            error AccountantWithRateProviders__OnlyCallableByBoringVault();
                            error AccountantWithRateProviders__UpdateDelayTooLarge();
                            error AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
                            //============================== EVENTS ===============================
                            event Paused();
                            event Unpaused();
                            event DelayInSecondsUpdated(uint24 oldDelay, uint24 newDelay);
                            event UpperBoundUpdated(uint16 oldBound, uint16 newBound);
                            event LowerBoundUpdated(uint16 oldBound, uint16 newBound);
                            event ManagementFeeUpdated(uint16 oldFee, uint16 newFee);
                            event PerformanceFeeUpdated(uint16 oldFee, uint16 newFee);
                            event PayoutAddressUpdated(address oldPayout, address newPayout);
                            event RateProviderUpdated(address asset, bool isPegged, address rateProvider);
                            event ExchangeRateUpdated(uint96 oldRate, uint96 newRate, uint64 currentTime);
                            event FeesClaimed(address indexed feeAsset, uint256 amount);
                            event HighwaterMarkReset();
                            //============================== IMMUTABLES ===============================
                            /**
                             * @notice The base asset rates are provided in.
                             */
                            ERC20 public immutable base;
                            /**
                             * @notice The decimals rates are provided in.
                             */
                            uint8 public immutable decimals;
                            /**
                             * @notice The BoringVault this accountant is working with.
                             *         Used to determine share supply for fee calculation.
                             */
                            BoringVault public immutable vault;
                            /**
                             * @notice One share of the BoringVault.
                             */
                            uint256 internal immutable ONE_SHARE;
                            constructor(
                                address _owner,
                                address _vault,
                                address payoutAddress,
                                uint96 startingExchangeRate,
                                address _base,
                                uint16 allowedExchangeRateChangeUpper,
                                uint16 allowedExchangeRateChangeLower,
                                uint24 minimumUpdateDelayInSeconds,
                                uint16 managementFee,
                                uint16 performanceFee
                            ) Auth(_owner, Authority(address(0))) {
                                base = ERC20(_base);
                                decimals = ERC20(_base).decimals();
                                vault = BoringVault(payable(_vault));
                                ONE_SHARE = 10 ** vault.decimals();
                                accountantState = AccountantState({
                                    payoutAddress: payoutAddress,
                                    highwaterMark: startingExchangeRate,
                                    feesOwedInBase: 0,
                                    totalSharesLastUpdate: uint128(vault.totalSupply()),
                                    exchangeRate: startingExchangeRate,
                                    allowedExchangeRateChangeUpper: allowedExchangeRateChangeUpper,
                                    allowedExchangeRateChangeLower: allowedExchangeRateChangeLower,
                                    lastUpdateTimestamp: uint64(block.timestamp),
                                    isPaused: false,
                                    minimumUpdateDelayInSeconds: minimumUpdateDelayInSeconds,
                                    managementFee: managementFee,
                                    performanceFee: performanceFee
                                });
                            }
                            // ========================================= ADMIN FUNCTIONS =========================================
                            /**
                             * @notice Pause this contract, which prevents future calls to `updateExchangeRate`, and any safe rate
                             *         calls will revert.
                             * @dev Callable by MULTISIG_ROLE.
                             */
                            function pause() external requiresAuth {
                                accountantState.isPaused = true;
                                emit Paused();
                            }
                            /**
                             * @notice Unpause this contract, which allows future calls to `updateExchangeRate`, and any safe rate
                             *         calls will stop reverting.
                             * @dev Callable by MULTISIG_ROLE.
                             */
                            function unpause() external requiresAuth {
                                accountantState.isPaused = false;
                                emit Unpaused();
                            }
                            /**
                             * @notice Update the minimum time delay between `updateExchangeRate` calls.
                             * @dev There are no input requirements, as it is possible the admin would want
                             *      the exchange rate updated as frequently as needed.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function updateDelay(uint24 minimumUpdateDelayInSeconds) external requiresAuth {
                                if (minimumUpdateDelayInSeconds > 14 days) revert AccountantWithRateProviders__UpdateDelayTooLarge();
                                uint24 oldDelay = accountantState.minimumUpdateDelayInSeconds;
                                accountantState.minimumUpdateDelayInSeconds = minimumUpdateDelayInSeconds;
                                emit DelayInSecondsUpdated(oldDelay, minimumUpdateDelayInSeconds);
                            }
                            /**
                             * @notice Update the allowed upper bound change of exchange rate between `updateExchangeRateCalls`.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function updateUpper(uint16 allowedExchangeRateChangeUpper) external requiresAuth {
                                if (allowedExchangeRateChangeUpper < 1e4) revert AccountantWithRateProviders__UpperBoundTooSmall();
                                uint16 oldBound = accountantState.allowedExchangeRateChangeUpper;
                                accountantState.allowedExchangeRateChangeUpper = allowedExchangeRateChangeUpper;
                                emit UpperBoundUpdated(oldBound, allowedExchangeRateChangeUpper);
                            }
                            /**
                             * @notice Update the allowed lower bound change of exchange rate between `updateExchangeRateCalls`.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function updateLower(uint16 allowedExchangeRateChangeLower) external requiresAuth {
                                if (allowedExchangeRateChangeLower > 1e4) revert AccountantWithRateProviders__LowerBoundTooLarge();
                                uint16 oldBound = accountantState.allowedExchangeRateChangeLower;
                                accountantState.allowedExchangeRateChangeLower = allowedExchangeRateChangeLower;
                                emit LowerBoundUpdated(oldBound, allowedExchangeRateChangeLower);
                            }
                            /**
                             * @notice Update the management fee to a new value.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function updateManagementFee(uint16 managementFee) external requiresAuth {
                                if (managementFee > 0.2e4) revert AccountantWithRateProviders__ManagementFeeTooLarge();
                                uint16 oldFee = accountantState.managementFee;
                                accountantState.managementFee = managementFee;
                                emit ManagementFeeUpdated(oldFee, managementFee);
                            }
                            /**
                             * @notice Update the performance fee to a new value.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function updatePerformanceFee(uint16 performanceFee) external requiresAuth {
                                if (performanceFee > 0.5e4) revert AccountantWithRateProviders__PerformanceFeeTooLarge();
                                uint16 oldFee = accountantState.performanceFee;
                                accountantState.performanceFee = performanceFee;
                                emit PerformanceFeeUpdated(oldFee, performanceFee);
                            }
                            /**
                             * @notice Update the payout address fees are sent to.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function updatePayoutAddress(address payoutAddress) external requiresAuth {
                                address oldPayout = accountantState.payoutAddress;
                                accountantState.payoutAddress = payoutAddress;
                                emit PayoutAddressUpdated(oldPayout, payoutAddress);
                            }
                            /**
                             * @notice Update the rate provider data for a specific `asset`.
                             * @dev Rate providers must return rates in terms of `base` or
                             * an asset pegged to base and they must use the same decimals
                             * as `asset`.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function setRateProviderData(ERC20 asset, bool isPeggedToBase, address rateProvider) external requiresAuth {
                                rateProviderData[asset] =
                                    RateProviderData({isPeggedToBase: isPeggedToBase, rateProvider: IRateProvider(rateProvider)});
                                emit RateProviderUpdated(address(asset), isPeggedToBase, rateProvider);
                            }
                            /**
                             * @notice Reset the highwater mark to the current exchange rate.
                             * @dev Callable by OWNER_ROLE.
                             */
                            function resetHighwaterMark() external requiresAuth {
                                AccountantState storage state = accountantState;
                                if (state.exchangeRate > state.highwaterMark) {
                                    revert AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
                                }
                                uint64 currentTime = uint64(block.timestamp);
                                uint256 currentTotalShares = vault.totalSupply();
                                _calculateFeesOwed(state, state.exchangeRate, state.exchangeRate, currentTotalShares, currentTime);
                                state.totalSharesLastUpdate = uint128(currentTotalShares);
                                state.highwaterMark = accountantState.exchangeRate;
                                state.lastUpdateTimestamp = currentTime;
                                emit HighwaterMarkReset();
                            }
                            // ========================================= UPDATE EXCHANGE RATE/FEES FUNCTIONS =========================================
                            /**
                             * @notice Updates this contract exchangeRate.
                             * @dev If new exchange rate is outside of accepted bounds, or if not enough time has passed, this
                             *      will pause the contract, and this function will NOT calculate fees owed.
                             * @dev Callable by UPDATE_EXCHANGE_RATE_ROLE.
                             */
                            function updateExchangeRate(uint96 newExchangeRate) external requiresAuth {
                                AccountantState storage state = accountantState;
                                if (state.isPaused) revert AccountantWithRateProviders__Paused();
                                uint64 currentTime = uint64(block.timestamp);
                                uint256 currentExchangeRate = state.exchangeRate;
                                uint256 currentTotalShares = vault.totalSupply();
                                if (
                                    currentTime < state.lastUpdateTimestamp + state.minimumUpdateDelayInSeconds
                                        || newExchangeRate > currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeUpper, 1e4)
                                        || newExchangeRate < currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeLower, 1e4)
                                ) {
                                    // Instead of reverting, pause the contract. This way the exchange rate updater is able to update the exchange rate
                                    // to a better value, and pause it.
                                    state.isPaused = true;
                                } else {
                                    _calculateFeesOwed(state, newExchangeRate, currentExchangeRate, currentTotalShares, currentTime);
                                }
                                state.exchangeRate = newExchangeRate;
                                state.totalSharesLastUpdate = uint128(currentTotalShares);
                                state.lastUpdateTimestamp = currentTime;
                                emit ExchangeRateUpdated(uint96(currentExchangeRate), newExchangeRate, currentTime);
                            }
                            /**
                             * @notice Claim pending fees.
                             * @dev This function must be called by the BoringVault.
                             * @dev This function will lose precision if the exchange rate
                             *      decimals is greater than the feeAsset's decimals.
                             */
                            function claimFees(ERC20 feeAsset) external {
                                if (msg.sender != address(vault)) revert AccountantWithRateProviders__OnlyCallableByBoringVault();
                                AccountantState storage state = accountantState;
                                if (state.isPaused) revert AccountantWithRateProviders__Paused();
                                if (state.feesOwedInBase == 0) revert AccountantWithRateProviders__ZeroFeesOwed();
                                // Determine amount of fees owed in feeAsset.
                                uint256 feesOwedInFeeAsset;
                                RateProviderData memory data = rateProviderData[feeAsset];
                                if (address(feeAsset) == address(base)) {
                                    feesOwedInFeeAsset = state.feesOwedInBase;
                                } else {
                                    uint8 feeAssetDecimals = ERC20(feeAsset).decimals();
                                    uint256 feesOwedInBaseUsingFeeAssetDecimals =
                                        changeDecimals(state.feesOwedInBase, decimals, feeAssetDecimals);
                                    if (data.isPeggedToBase) {
                                        feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals;
                                    } else {
                                        uint256 rate = data.rateProvider.getRate();
                                        feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals.mulDivDown(10 ** feeAssetDecimals, rate);
                                    }
                                }
                                // Zero out fees owed.
                                state.feesOwedInBase = 0;
                                // Transfer fee asset to payout address.
                                feeAsset.safeTransferFrom(msg.sender, state.payoutAddress, feesOwedInFeeAsset);
                                emit FeesClaimed(address(feeAsset), feesOwedInFeeAsset);
                            }
                            // ========================================= RATE FUNCTIONS =========================================
                            /**
                             * @notice Get this BoringVault's current rate in the base.
                             */
                            function getRate() public view returns (uint256 rate) {
                                rate = accountantState.exchangeRate;
                            }
                            /**
                             * @notice Get this BoringVault's current rate in the base.
                             * @dev Revert if paused.
                             */
                            function getRateSafe() external view returns (uint256 rate) {
                                if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
                                rate = getRate();
                            }
                            /**
                             * @notice Get this BoringVault's current rate in the provided quote.
                             * @dev `quote` must have its RateProviderData set, else this will revert.
                             * @dev This function will lose precision if the exchange rate
                             *      decimals is greater than the quote's decimals.
                             */
                            function getRateInQuote(ERC20 quote) public view returns (uint256 rateInQuote) {
                                if (address(quote) == address(base)) {
                                    rateInQuote = accountantState.exchangeRate;
                                } else {
                                    RateProviderData memory data = rateProviderData[quote];
                                    uint8 quoteDecimals = ERC20(quote).decimals();
                                    uint256 exchangeRateInQuoteDecimals = changeDecimals(accountantState.exchangeRate, decimals, quoteDecimals);
                                    if (data.isPeggedToBase) {
                                        rateInQuote = exchangeRateInQuoteDecimals;
                                    } else {
                                        uint256 quoteRate = data.rateProvider.getRate();
                                        uint256 oneQuote = 10 ** quoteDecimals;
                                        rateInQuote = oneQuote.mulDivDown(exchangeRateInQuoteDecimals, quoteRate);
                                    }
                                }
                            }
                            /**
                             * @notice Get this BoringVault's current rate in the provided quote.
                             * @dev `quote` must have its RateProviderData set, else this will revert.
                             * @dev Revert if paused.
                             */
                            function getRateInQuoteSafe(ERC20 quote) external view returns (uint256 rateInQuote) {
                                if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
                                rateInQuote = getRateInQuote(quote);
                            }
                            // ========================================= INTERNAL HELPER FUNCTIONS =========================================
                            /**
                             * @notice Used to change the decimals of precision used for an amount.
                             */
                            function changeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {
                                if (fromDecimals == toDecimals) {
                                    return amount;
                                } else if (fromDecimals < toDecimals) {
                                    return amount * 10 ** (toDecimals - fromDecimals);
                                } else {
                                    return amount / 10 ** (fromDecimals - toDecimals);
                                }
                            }
                            /**
                             * @notice Calculate fees owed in base.
                             * @dev This function will update the highwater mark if the new exchange rate is higher.
                             */
                            function _calculateFeesOwed(
                                AccountantState storage state,
                                uint96 newExchangeRate,
                                uint256 currentExchangeRate,
                                uint256 currentTotalShares,
                                uint64 currentTime
                            ) internal {
                                // Only update fees if we are not paused.
                                // Update fee accounting.
                                uint256 shareSupplyToUse = currentTotalShares;
                                // Use the minimum between current total supply and total supply for last update.
                                if (state.totalSharesLastUpdate < shareSupplyToUse) {
                                    shareSupplyToUse = state.totalSharesLastUpdate;
                                }
                                // Determine management fees owned.
                                uint256 timeDelta = currentTime - state.lastUpdateTimestamp;
                                uint256 minimumAssets = newExchangeRate > currentExchangeRate
                                    ? shareSupplyToUse.mulDivDown(currentExchangeRate, ONE_SHARE)
                                    : shareSupplyToUse.mulDivDown(newExchangeRate, ONE_SHARE);
                                uint256 managementFeesAnnual = minimumAssets.mulDivDown(state.managementFee, 1e4);
                                uint256 newFeesOwedInBase = managementFeesAnnual.mulDivDown(timeDelta, 365 days);
                                // Account for performance fees.
                                if (newExchangeRate > state.highwaterMark) {
                                    if (state.performanceFee > 0) {
                                        uint256 changeInExchangeRate = newExchangeRate - state.highwaterMark;
                                        uint256 yieldEarned = changeInExchangeRate.mulDivDown(shareSupplyToUse, ONE_SHARE);
                                        uint256 performanceFeesOwedInBase = yieldEarned.mulDivDown(state.performanceFee, 1e4);
                                        newFeesOwedInBase += performanceFeesOwedInBase;
                                    }
                                    // Always update the highwater mark if the new exchange rate is higher.
                                    // This way if we are not iniitiall taking performance fees, we can start taking them
                                    // without back charging them on past performance.
                                    state.highwaterMark = newExchangeRate;
                                }
                                state.feesOwedInBase += uint128(newFeesOwedInBase);
                            }
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        /// @notice Arithmetic library with operations for fixed-point numbers.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
                        /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
                        library FixedPointMathLib {
                            /*//////////////////////////////////////////////////////////////
                                            SIMPLIFIED FIXED POINT OPERATIONS
                            //////////////////////////////////////////////////////////////*/
                            uint256 internal constant MAX_UINT256 = 2**256 - 1;
                            uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
                            function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
                                return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
                            }
                            function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
                                return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
                            }
                            function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
                                return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
                            }
                            function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
                                return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
                            }
                            /*//////////////////////////////////////////////////////////////
                                            LOW LEVEL FIXED POINT OPERATIONS
                            //////////////////////////////////////////////////////////////*/
                            function mulDivDown(
                                uint256 x,
                                uint256 y,
                                uint256 denominator
                            ) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                                    if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                                        revert(0, 0)
                                    }
                                    // Divide x * y by the denominator.
                                    z := div(mul(x, y), denominator)
                                }
                            }
                            function mulDivUp(
                                uint256 x,
                                uint256 y,
                                uint256 denominator
                            ) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                                    if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                                        revert(0, 0)
                                    }
                                    // If x * y modulo the denominator is strictly greater than 0,
                                    // 1 is added to round up the division of x * y by the denominator.
                                    z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
                                }
                            }
                            function rpow(
                                uint256 x,
                                uint256 n,
                                uint256 scalar
                            ) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    switch x
                                    case 0 {
                                        switch n
                                        case 0 {
                                            // 0 ** 0 = 1
                                            z := scalar
                                        }
                                        default {
                                            // 0 ** n = 0
                                            z := 0
                                        }
                                    }
                                    default {
                                        switch mod(n, 2)
                                        case 0 {
                                            // If n is even, store scalar in z for now.
                                            z := scalar
                                        }
                                        default {
                                            // If n is odd, store x in z for now.
                                            z := x
                                        }
                                        // Shifting right by 1 is like dividing by 2.
                                        let half := shr(1, scalar)
                                        for {
                                            // Shift n right by 1 before looping to halve it.
                                            n := shr(1, n)
                                        } n {
                                            // Shift n right by 1 each iteration to halve it.
                                            n := shr(1, n)
                                        } {
                                            // Revert immediately if x ** 2 would overflow.
                                            // Equivalent to iszero(eq(div(xx, x), x)) here.
                                            if shr(128, x) {
                                                revert(0, 0)
                                            }
                                            // Store x squared.
                                            let xx := mul(x, x)
                                            // Round to the nearest number.
                                            let xxRound := add(xx, half)
                                            // Revert if xx + half overflowed.
                                            if lt(xxRound, xx) {
                                                revert(0, 0)
                                            }
                                            // Set x to scaled xxRound.
                                            x := div(xxRound, scalar)
                                            // If n is even:
                                            if mod(n, 2) {
                                                // Compute z * x.
                                                let zx := mul(z, x)
                                                // If z * x overflowed:
                                                if iszero(eq(div(zx, x), z)) {
                                                    // Revert if x is non-zero.
                                                    if iszero(iszero(x)) {
                                                        revert(0, 0)
                                                    }
                                                }
                                                // Round to the nearest number.
                                                let zxRound := add(zx, half)
                                                // Revert if zx + half overflowed.
                                                if lt(zxRound, zx) {
                                                    revert(0, 0)
                                                }
                                                // Return properly scaled zxRound.
                                                z := div(zxRound, scalar)
                                            }
                                        }
                                    }
                                }
                            }
                            /*//////////////////////////////////////////////////////////////
                                                GENERAL NUMBER UTILITIES
                            //////////////////////////////////////////////////////////////*/
                            function sqrt(uint256 x) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    let y := x // We start y at x, which will help us make our initial estimate.
                                    z := 181 // The "correct" value is 1, but this saves a multiplication later.
                                    // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                                    // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
                                    // We check y >= 2^(k + 8) but shift right by k bits
                                    // each branch to ensure that if x >= 256, then y >= 256.
                                    if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                                        y := shr(128, y)
                                        z := shl(64, z)
                                    }
                                    if iszero(lt(y, 0x1000000000000000000)) {
                                        y := shr(64, y)
                                        z := shl(32, z)
                                    }
                                    if iszero(lt(y, 0x10000000000)) {
                                        y := shr(32, y)
                                        z := shl(16, z)
                                    }
                                    if iszero(lt(y, 0x1000000)) {
                                        y := shr(16, y)
                                        z := shl(8, z)
                                    }
                                    // Goal was to get z*z*y within a small factor of x. More iterations could
                                    // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
                                    // We ensured y >= 256 so that the relative difference between y and y+1 is small.
                                    // That's not possible if x < 256 but we can just verify those cases exhaustively.
                                    // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
                                    // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
                                    // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
                                    // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
                                    // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
                                    // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
                                    // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
                                    // There is no overflow risk here since y < 2^136 after the first branch above.
                                    z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
                                    // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    z := shr(1, add(z, div(x, z)))
                                    // If x+1 is a perfect square, the Babylonian method cycles between
                                    // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
                                    // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                                    // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                                    // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                                    z := sub(z, lt(div(x, z), z))
                                }
                            }
                            function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Mod x by y. Note this will return
                                    // 0 instead of reverting if y is zero.
                                    z := mod(x, y)
                                }
                            }
                            function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Divide x by y. Note this will return
                                    // 0 instead of reverting if y is zero.
                                    r := div(x, y)
                                }
                            }
                            function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Add 1 to x * y if x % y > 0. Note this will
                                    // return 0 instead of reverting if y is zero.
                                    z := add(gt(mod(x, y), 0), div(x, y))
                                }
                            }
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        import {ERC20} from "../tokens/ERC20.sol";
                        /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
                        /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
                        /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
                        library SafeTransferLib {
                            /*//////////////////////////////////////////////////////////////
                                                     ETH OPERATIONS
                            //////////////////////////////////////////////////////////////*/
                            function safeTransferETH(address to, uint256 amount) internal {
                                bool success;
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Transfer the ETH and store if it succeeded or not.
                                    success := call(gas(), to, amount, 0, 0, 0, 0)
                                }
                                require(success, "ETH_TRANSFER_FAILED");
                            }
                            /*//////////////////////////////////////////////////////////////
                                                    ERC20 OPERATIONS
                            //////////////////////////////////////////////////////////////*/
                            function safeTransferFrom(
                                ERC20 token,
                                address from,
                                address to,
                                uint256 amount
                            ) internal {
                                bool success;
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Get a pointer to some free memory.
                                    let freeMemoryPointer := mload(0x40)
                                    // Write the abi-encoded calldata into memory, beginning with the function selector.
                                    mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                                    mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
                                    mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                                    mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                                    success := and(
                                        // Set success to whether the call reverted, if not we check it either
                                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                                        or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                        // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                                        // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                        // Counterintuitively, this call must be positioned second to the or() call in the
                                        // surrounding and() call or else returndatasize() will be zero during the computation.
                                        call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                                    )
                                }
                                require(success, "TRANSFER_FROM_FAILED");
                            }
                            function safeTransfer(
                                ERC20 token,
                                address to,
                                uint256 amount
                            ) internal {
                                bool success;
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Get a pointer to some free memory.
                                    let freeMemoryPointer := mload(0x40)
                                    // Write the abi-encoded calldata into memory, beginning with the function selector.
                                    mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                                    mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                                    mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                                    success := and(
                                        // Set success to whether the call reverted, if not we check it either
                                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                                        or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                        // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                                        // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                        // Counterintuitively, this call must be positioned second to the or() call in the
                                        // surrounding and() call or else returndatasize() will be zero during the computation.
                                        call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                                    )
                                }
                                require(success, "TRANSFER_FAILED");
                            }
                            function safeApprove(
                                ERC20 token,
                                address to,
                                uint256 amount
                            ) internal {
                                bool success;
                                /// @solidity memory-safe-assembly
                                assembly {
                                    // Get a pointer to some free memory.
                                    let freeMemoryPointer := mload(0x40)
                                    // Write the abi-encoded calldata into memory, beginning with the function selector.
                                    mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                                    mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                                    mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                                    success := and(
                                        // Set success to whether the call reverted, if not we check it either
                                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                                        or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                                        // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                                        // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                        // Counterintuitively, this call must be positioned second to the or() call in the
                                        // surrounding and() call or else returndatasize() will be zero during the computation.
                                        call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                                    )
                                }
                                require(success, "APPROVE_FAILED");
                            }
                        }
                        // SPDX-License-Identifier: UNLICENSED
                        pragma solidity 0.8.21;
                        interface BeforeTransferHook {
                            function beforeTransfer(address from, address to, address operator) external view;
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        /// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
                        /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
                        abstract contract Auth {
                            event OwnershipTransferred(address indexed user, address indexed newOwner);
                            event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
                            address public owner;
                            Authority public authority;
                            constructor(address _owner, Authority _authority) {
                                owner = _owner;
                                authority = _authority;
                                emit OwnershipTransferred(msg.sender, _owner);
                                emit AuthorityUpdated(msg.sender, _authority);
                            }
                            modifier requiresAuth() virtual {
                                require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
                                _;
                            }
                            function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
                                Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
                                // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
                                // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
                                return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
                            }
                            function setAuthority(Authority newAuthority) public virtual {
                                // We check if the caller is the owner first because we want to ensure they can
                                // always swap out the authority even if it's reverting or using up a lot of gas.
                                require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
                                authority = newAuthority;
                                emit AuthorityUpdated(msg.sender, newAuthority);
                            }
                            function transferOwnership(address newOwner) public virtual requiresAuth {
                                owner = newOwner;
                                emit OwnershipTransferred(msg.sender, newOwner);
                            }
                        }
                        /// @notice A generic interface for a contract which provides authorization data to an Auth instance.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
                        /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
                        interface Authority {
                            function canCall(
                                address user,
                                address target,
                                bytes4 functionSig
                            ) external view returns (bool);
                        }
                        // SPDX-License-Identifier: AGPL-3.0-only
                        pragma solidity >=0.8.0;
                        /// @notice Gas optimized reentrancy protection for smart contracts.
                        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
                        /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
                        abstract contract ReentrancyGuard {
                            uint256 private locked = 1;
                            modifier nonReentrant() virtual {
                                require(locked == 1, "REENTRANCY");
                                locked = 2;
                                _;
                                locked = 1;
                            }
                        }
                        // SPDX-License-Identifier: UNLICENSED
                        pragma solidity 0.8.21;
                        interface IPausable {
                            function pause() external;
                            function unpause() external;
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
                        pragma solidity ^0.8.20;
                        /**
                         * @dev Collection of functions related to the address type
                         */
                        library Address {
                            /**
                             * @dev The ETH balance of the account is not enough to perform the operation.
                             */
                            error AddressInsufficientBalance(address account);
                            /**
                             * @dev There's no code at `target` (it is not a contract).
                             */
                            error AddressEmptyCode(address target);
                            /**
                             * @dev A call to an address target failed. The target may have reverted.
                             */
                            error FailedInnerCall();
                            /**
                             * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                             */
                            function sendValue(address payable recipient, uint256 amount) internal {
                                if (address(this).balance < amount) {
                                    revert AddressInsufficientBalance(address(this));
                                }
                                (bool success, ) = recipient.call{value: amount}("");
                                if (!success) {
                                    revert FailedInnerCall();
                                }
                            }
                            /**
                             * @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 or custom error, it is bubbled
                             * up by this function (like regular Solidity function calls). However, if
                             * the call reverted with no returned reason, this function reverts with a
                             * {FailedInnerCall} error.
                             *
                             * 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.
                             */
                            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                return functionCallWithValue(target, data, 0);
                            }
                            /**
                             * @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`.
                             */
                            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                                if (address(this).balance < value) {
                                    revert AddressInsufficientBalance(address(this));
                                }
                                (bool success, bytes memory returndata) = target.call{value: value}(data);
                                return verifyCallResultFromTarget(target, success, returndata);
                            }
                            /**
                             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                             * but performing a static call.
                             */
                            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                                (bool success, bytes memory returndata) = target.staticcall(data);
                                return verifyCallResultFromTarget(target, success, returndata);
                            }
                            /**
                             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                             * but performing a delegate call.
                             */
                            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                                (bool success, bytes memory returndata) = target.delegatecall(data);
                                return verifyCallResultFromTarget(target, success, returndata);
                            }
                            /**
                             * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                             * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
                             * unsuccessful call.
                             */
                            function verifyCallResultFromTarget(
                                address target,
                                bool success,
                                bytes memory returndata
                            ) internal view returns (bytes memory) {
                                if (!success) {
                                    _revert(returndata);
                                } else {
                                    // only check if target is a contract if the call was successful and the return data is empty
                                    // otherwise we already know that it was a contract
                                    if (returndata.length == 0 && target.code.length == 0) {
                                        revert AddressEmptyCode(target);
                                    }
                                    return returndata;
                                }
                            }
                            /**
                             * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                             * revert reason or with a default {FailedInnerCall} error.
                             */
                            function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                                if (!success) {
                                    _revert(returndata);
                                } else {
                                    return returndata;
                                }
                            }
                            /**
                             * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
                             */
                            function _revert(bytes memory returndata) private pure {
                                // 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
                                    /// @solidity memory-safe-assembly
                                    assembly {
                                        let returndata_size := mload(returndata)
                                        revert(add(32, returndata), returndata_size)
                                    }
                                } else {
                                    revert FailedInnerCall();
                                }
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
                        pragma solidity ^0.8.20;
                        import {IERC721Receiver} from "../IERC721Receiver.sol";
                        /**
                         * @dev Implementation of the {IERC721Receiver} interface.
                         *
                         * Accepts all token transfers.
                         * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
                         * {IERC721-setApprovalForAll}.
                         */
                        abstract contract ERC721Holder is IERC721Receiver {
                            /**
                             * @dev See {IERC721Receiver-onERC721Received}.
                             *
                             * Always returns `IERC721Receiver.onERC721Received.selector`.
                             */
                            function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
                                return this.onERC721Received.selector;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/utils/ERC1155Holder.sol)
                        pragma solidity ^0.8.20;
                        import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
                        import {IERC1155Receiver} from "../IERC1155Receiver.sol";
                        /**
                         * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
                         *
                         * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
                         * stuck.
                         */
                        abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
                            /**
                             * @dev See {IERC165-supportsInterface}.
                             */
                            function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                                return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
                            }
                            function onERC1155Received(
                                address,
                                address,
                                uint256,
                                uint256,
                                bytes memory
                            ) public virtual override returns (bytes4) {
                                return this.onERC1155Received.selector;
                            }
                            function onERC1155BatchReceived(
                                address,
                                address,
                                uint256[] memory,
                                uint256[] memory,
                                bytes memory
                            ) public virtual override returns (bytes4) {
                                return this.onERC1155BatchReceived.selector;
                            }
                        }
                        // SPDX-License-Identifier: UNLICENSED
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        pragma solidity ^0.8.0;
                        interface IRateProvider {
                            function getRate() external view returns (uint256);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
                        pragma solidity ^0.8.20;
                        /**
                         * @title ERC721 token receiver interface
                         * @dev Interface for any contract that wants to support safeTransfers
                         * from ERC721 asset contracts.
                         */
                        interface IERC721Receiver {
                            /**
                             * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                             * by `operator` from `from`, this function is called.
                             *
                             * It must return its Solidity selector to confirm the token transfer.
                             * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
                             * reverted.
                             *
                             * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
                             */
                            function onERC721Received(
                                address operator,
                                address from,
                                uint256 tokenId,
                                bytes calldata data
                            ) external returns (bytes4);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
                        pragma solidity ^0.8.20;
                        import {IERC165} from "./IERC165.sol";
                        /**
                         * @dev Implementation of the {IERC165} interface.
                         *
                         * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
                         * for the additional interface id that will be supported. For example:
                         *
                         * ```solidity
                         * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                         *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
                         * }
                         * ```
                         */
                        abstract contract ERC165 is IERC165 {
                            /**
                             * @dev See {IERC165-supportsInterface}.
                             */
                            function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                                return interfaceId == type(IERC165).interfaceId;
                            }
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
                        pragma solidity ^0.8.20;
                        import {IERC165} from "../../utils/introspection/IERC165.sol";
                        /**
                         * @dev Interface that must be implemented by smart contracts in order to receive
                         * ERC-1155 token transfers.
                         */
                        interface IERC1155Receiver is IERC165 {
                            /**
                             * @dev Handles the receipt of a single ERC1155 token type. This function is
                             * called at the end of a `safeTransferFrom` after the balance has been updated.
                             *
                             * NOTE: To accept the transfer, this must return
                             * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                             * (i.e. 0xf23a6e61, or its own function selector).
                             *
                             * @param operator The address which initiated the transfer (i.e. msg.sender)
                             * @param from The address which previously owned the token
                             * @param id The ID of the token being transferred
                             * @param value The amount of tokens being transferred
                             * @param data Additional data with no specified format
                             * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
                             */
                            function onERC1155Received(
                                address operator,
                                address from,
                                uint256 id,
                                uint256 value,
                                bytes calldata data
                            ) external returns (bytes4);
                            /**
                             * @dev Handles the receipt of a multiple ERC1155 token types. This function
                             * is called at the end of a `safeBatchTransferFrom` after the balances have
                             * been updated.
                             *
                             * NOTE: To accept the transfer(s), this must return
                             * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                             * (i.e. 0xbc197c81, or its own function selector).
                             *
                             * @param operator The address which initiated the batch transfer (i.e. msg.sender)
                             * @param from The address which previously owned the token
                             * @param ids An array containing ids of each token being transferred (order and length must match values array)
                             * @param values An array containing amounts of each token being transferred (order and length must match ids array)
                             * @param data Additional data with no specified format
                             * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
                             */
                            function onERC1155BatchReceived(
                                address operator,
                                address from,
                                uint256[] calldata ids,
                                uint256[] calldata values,
                                bytes calldata data
                            ) external returns (bytes4);
                        }
                        // SPDX-License-Identifier: MIT
                        // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
                        pragma solidity ^0.8.20;
                        /**
                         * @dev Interface of the ERC165 standard, as defined in the
                         * https://eips.ethereum.org/EIPS/eip-165[EIP].
                         *
                         * Implementers can declare support of contract interfaces, which can then be
                         * queried by others ({ERC165Checker}).
                         *
                         * For an implementation, see {ERC165}.
                         */
                        interface IERC165 {
                            /**
                             * @dev Returns true if this contract implements the interface defined by
                             * `interfaceId`. See the corresponding
                             * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                             * to learn more about how these ids are created.
                             *
                             * This function call must use less than 30 000 gas.
                             */
                            function supportsInterface(bytes4 interfaceId) external view returns (bool);
                        }