ETH Price: $1,942.45 (-5.49%)

Transaction Decoder

Block:
24393289 at Feb-05-2026 09:23:59 PM +UTC
Transaction Fee:
0.001683834350071725 ETH $3.27
Gas Used:
156,753 Gas / 10.741959325 Gwei

Emitted Events:

1089 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000005f02b775a570cd9e89875b50f63246bab3e968ff, 0x0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000146e7803 )
1090 InitializableImmutableAdminUpgradeabilityProxy.0x4cf25bc1d991c17529c25213d3cc0cda295eeaad5f13f361969b12ea48015f90( 0x4cf25bc1d991c17529c25213d3cc0cda295eeaad5f13f361969b12ea48015f90, 0x0000000000000000000000005f02b775a570cd9e89875b50f63246bab3e968ff, 0x0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000146e7803, 00000000000000000000000000000000000000000000000000000000002fc362, 000000000000000000000000000000000000000003ece4a92ee903bd66ecc7e4 )
1091 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 00000000000000000000000000000000000000000011ab0b2e67470e3c2006af, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000001db51d25aee5f80de8dc4a, 000000000000000000000000000000000000000003bb2fd2447cf6800c866c51, 000000000000000000000000000000000000000003ece4a92ee903bd66ecc7e4 )
1092 TetherToken.Transfer( from=[Sender] 0x5f02b775a570cd9e89875b50f63246bab3e968ff, to=InitializableImmutableAdminUpgradeabilityProxy, value=345914213 )
1093 InitializableImmutableAdminUpgradeabilityProxy.0xa534c8dbe71f871f9f3530e97a74601fea17b426cae02e1c5aee42c96c784051( 0xa534c8dbe71f871f9f3530e97a74601fea17b426cae02e1c5aee42c96c784051, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0x0000000000000000000000005f02b775a570cd9e89875b50f63246bab3e968ff, 0x0000000000000000000000005f02b775a570cd9e89875b50f63246bab3e968ff, 00000000000000000000000000000000000000000000000000000000149e3b65, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
86.276829939084429771 Eth86.276896404065821236 Eth0.000066464981391465
0x5f02b775...Ab3E968FF
0.260704724741408381 Eth
Nonce: 30
0.259020890391336656 Eth
Nonce: 31
0.001683834350071725
0x6df1C1E3...3772f45A8
0x87870Bca...50B4fA4E2
(Aave: Pool V3)
0xdAC17F95...13D831ec7

Execution Trace

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

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

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

                  File 4 of 8: TetherToken
                  pragma solidity ^0.4.17;
                  
                  /**
                   * @title SafeMath
                   * @dev Math operations with safety checks that throw on error
                   */
                  library SafeMath {
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          if (a == 0) {
                              return 0;
                          }
                          uint256 c = a * b;
                          assert(c / a == b);
                          return c;
                      }
                  
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          // assert(b > 0); // Solidity automatically throws when dividing by 0
                          uint256 c = a / b;
                          // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                          return c;
                      }
                  
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          assert(b <= a);
                          return a - b;
                      }
                  
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          uint256 c = a + b;
                          assert(c >= a);
                          return c;
                      }
                  }
                  
                  /**
                   * @title Ownable
                   * @dev The Ownable contract has an owner address, and provides basic authorization control
                   * functions, this simplifies the implementation of "user permissions".
                   */
                  contract Ownable {
                      address public owner;
                  
                      /**
                        * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                        * account.
                        */
                      function Ownable() public {
                          owner = msg.sender;
                      }
                  
                      /**
                        * @dev Throws if called by any account other than the owner.
                        */
                      modifier onlyOwner() {
                          require(msg.sender == owner);
                          _;
                      }
                  
                      /**
                      * @dev Allows the current owner to transfer control of the contract to a newOwner.
                      * @param newOwner The address to transfer ownership to.
                      */
                      function transferOwnership(address newOwner) public onlyOwner {
                          if (newOwner != address(0)) {
                              owner = newOwner;
                          }
                      }
                  
                  }
                  
                  /**
                   * @title ERC20Basic
                   * @dev Simpler version of ERC20 interface
                   * @dev see https://github.com/ethereum/EIPs/issues/20
                   */
                  contract ERC20Basic {
                      uint public _totalSupply;
                      function totalSupply() public constant returns (uint);
                      function balanceOf(address who) public constant returns (uint);
                      function transfer(address to, uint value) public;
                      event Transfer(address indexed from, address indexed to, uint value);
                  }
                  
                  /**
                   * @title ERC20 interface
                   * @dev see https://github.com/ethereum/EIPs/issues/20
                   */
                  contract ERC20 is ERC20Basic {
                      function allowance(address owner, address spender) public constant returns (uint);
                      function transferFrom(address from, address to, uint value) public;
                      function approve(address spender, uint value) public;
                      event Approval(address indexed owner, address indexed spender, uint value);
                  }
                  
                  /**
                   * @title Basic token
                   * @dev Basic version of StandardToken, with no allowances.
                   */
                  contract BasicToken is Ownable, ERC20Basic {
                      using SafeMath for uint;
                  
                      mapping(address => uint) public balances;
                  
                      // additional variables for use if transaction fees ever became necessary
                      uint public basisPointsRate = 0;
                      uint public maximumFee = 0;
                  
                      /**
                      * @dev Fix for the ERC20 short address attack.
                      */
                      modifier onlyPayloadSize(uint size) {
                          require(!(msg.data.length < size + 4));
                          _;
                      }
                  
                      /**
                      * @dev transfer token for a specified address
                      * @param _to The address to transfer to.
                      * @param _value The amount to be transferred.
                      */
                      function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
                          uint fee = (_value.mul(basisPointsRate)).div(10000);
                          if (fee > maximumFee) {
                              fee = maximumFee;
                          }
                          uint sendAmount = _value.sub(fee);
                          balances[msg.sender] = balances[msg.sender].sub(_value);
                          balances[_to] = balances[_to].add(sendAmount);
                          if (fee > 0) {
                              balances[owner] = balances[owner].add(fee);
                              Transfer(msg.sender, owner, fee);
                          }
                          Transfer(msg.sender, _to, sendAmount);
                      }
                  
                      /**
                      * @dev Gets the balance of the specified address.
                      * @param _owner The address to query the the balance of.
                      * @return An uint representing the amount owned by the passed address.
                      */
                      function balanceOf(address _owner) public constant returns (uint balance) {
                          return balances[_owner];
                      }
                  
                  }
                  
                  /**
                   * @title Standard ERC20 token
                   *
                   * @dev Implementation of the basic standard token.
                   * @dev https://github.com/ethereum/EIPs/issues/20
                   * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
                   */
                  contract StandardToken is BasicToken, ERC20 {
                  
                      mapping (address => mapping (address => uint)) public allowed;
                  
                      uint public constant MAX_UINT = 2**256 - 1;
                  
                      /**
                      * @dev Transfer tokens from one address to another
                      * @param _from address The address which you want to send tokens from
                      * @param _to address The address which you want to transfer to
                      * @param _value uint the amount of tokens to be transferred
                      */
                      function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
                          var _allowance = allowed[_from][msg.sender];
                  
                          // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                          // if (_value > _allowance) throw;
                  
                          uint fee = (_value.mul(basisPointsRate)).div(10000);
                          if (fee > maximumFee) {
                              fee = maximumFee;
                          }
                          if (_allowance < MAX_UINT) {
                              allowed[_from][msg.sender] = _allowance.sub(_value);
                          }
                          uint sendAmount = _value.sub(fee);
                          balances[_from] = balances[_from].sub(_value);
                          balances[_to] = balances[_to].add(sendAmount);
                          if (fee > 0) {
                              balances[owner] = balances[owner].add(fee);
                              Transfer(_from, owner, fee);
                          }
                          Transfer(_from, _to, sendAmount);
                      }
                  
                      /**
                      * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                      * @param _spender The address which will spend the funds.
                      * @param _value The amount of tokens to be spent.
                      */
                      function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                  
                          // To change the approve amount you first have to reduce the addresses`
                          //  allowance to zero by calling `approve(_spender, 0)` if it is not
                          //  already 0 to mitigate the race condition described here:
                          //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                          require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
                  
                          allowed[msg.sender][_spender] = _value;
                          Approval(msg.sender, _spender, _value);
                      }
                  
                      /**
                      * @dev Function to check the amount of tokens than an owner allowed to a spender.
                      * @param _owner address The address which owns the funds.
                      * @param _spender address The address which will spend the funds.
                      * @return A uint specifying the amount of tokens still available for the spender.
                      */
                      function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                          return allowed[_owner][_spender];
                      }
                  
                  }
                  
                  
                  /**
                   * @title Pausable
                   * @dev Base contract which allows children to implement an emergency stop mechanism.
                   */
                  contract Pausable is Ownable {
                    event Pause();
                    event Unpause();
                  
                    bool public paused = false;
                  
                  
                    /**
                     * @dev Modifier to make a function callable only when the contract is not paused.
                     */
                    modifier whenNotPaused() {
                      require(!paused);
                      _;
                    }
                  
                    /**
                     * @dev Modifier to make a function callable only when the contract is paused.
                     */
                    modifier whenPaused() {
                      require(paused);
                      _;
                    }
                  
                    /**
                     * @dev called by the owner to pause, triggers stopped state
                     */
                    function pause() onlyOwner whenNotPaused public {
                      paused = true;
                      Pause();
                    }
                  
                    /**
                     * @dev called by the owner to unpause, returns to normal state
                     */
                    function unpause() onlyOwner whenPaused public {
                      paused = false;
                      Unpause();
                    }
                  }
                  
                  contract BlackList is Ownable, BasicToken {
                  
                      /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
                      function getBlackListStatus(address _maker) external constant returns (bool) {
                          return isBlackListed[_maker];
                      }
                  
                      function getOwner() external constant returns (address) {
                          return owner;
                      }
                  
                      mapping (address => bool) public isBlackListed;
                      
                      function addBlackList (address _evilUser) public onlyOwner {
                          isBlackListed[_evilUser] = true;
                          AddedBlackList(_evilUser);
                      }
                  
                      function removeBlackList (address _clearedUser) public onlyOwner {
                          isBlackListed[_clearedUser] = false;
                          RemovedBlackList(_clearedUser);
                      }
                  
                      function destroyBlackFunds (address _blackListedUser) public onlyOwner {
                          require(isBlackListed[_blackListedUser]);
                          uint dirtyFunds = balanceOf(_blackListedUser);
                          balances[_blackListedUser] = 0;
                          _totalSupply -= dirtyFunds;
                          DestroyedBlackFunds(_blackListedUser, dirtyFunds);
                      }
                  
                      event DestroyedBlackFunds(address _blackListedUser, uint _balance);
                  
                      event AddedBlackList(address _user);
                  
                      event RemovedBlackList(address _user);
                  
                  }
                  
                  contract UpgradedStandardToken is StandardToken{
                      // those methods are called by the legacy contract
                      // and they must ensure msg.sender to be the contract address
                      function transferByLegacy(address from, address to, uint value) public;
                      function transferFromByLegacy(address sender, address from, address spender, uint value) public;
                      function approveByLegacy(address from, address spender, uint value) public;
                  }
                  
                  contract TetherToken is Pausable, StandardToken, BlackList {
                  
                      string public name;
                      string public symbol;
                      uint public decimals;
                      address public upgradedAddress;
                      bool public deprecated;
                  
                      //  The contract can be initialized with a number of tokens
                      //  All the tokens are deposited to the owner address
                      //
                      // @param _balance Initial supply of the contract
                      // @param _name Token Name
                      // @param _symbol Token symbol
                      // @param _decimals Token decimals
                      function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
                          _totalSupply = _initialSupply;
                          name = _name;
                          symbol = _symbol;
                          decimals = _decimals;
                          balances[owner] = _initialSupply;
                          deprecated = false;
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function transfer(address _to, uint _value) public whenNotPaused {
                          require(!isBlackListed[msg.sender]);
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
                          } else {
                              return super.transfer(_to, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
                          require(!isBlackListed[_from]);
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
                          } else {
                              return super.transferFrom(_from, _to, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function balanceOf(address who) public constant returns (uint) {
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).balanceOf(who);
                          } else {
                              return super.balanceOf(who);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
                          } else {
                              return super.approve(_spender, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                          if (deprecated) {
                              return StandardToken(upgradedAddress).allowance(_owner, _spender);
                          } else {
                              return super.allowance(_owner, _spender);
                          }
                      }
                  
                      // deprecate current contract in favour of a new one
                      function deprecate(address _upgradedAddress) public onlyOwner {
                          deprecated = true;
                          upgradedAddress = _upgradedAddress;
                          Deprecate(_upgradedAddress);
                      }
                  
                      // deprecate current contract if favour of a new one
                      function totalSupply() public constant returns (uint) {
                          if (deprecated) {
                              return StandardToken(upgradedAddress).totalSupply();
                          } else {
                              return _totalSupply;
                          }
                      }
                  
                      // Issue a new amount of tokens
                      // these tokens are deposited into the owner address
                      //
                      // @param _amount Number of tokens to be issued
                      function issue(uint amount) public onlyOwner {
                          require(_totalSupply + amount > _totalSupply);
                          require(balances[owner] + amount > balances[owner]);
                  
                          balances[owner] += amount;
                          _totalSupply += amount;
                          Issue(amount);
                      }
                  
                      // Redeem tokens.
                      // These tokens are withdrawn from the owner address
                      // if the balance must be enough to cover the redeem
                      // or the call will fail.
                      // @param _amount Number of tokens to be issued
                      function redeem(uint amount) public onlyOwner {
                          require(_totalSupply >= amount);
                          require(balances[owner] >= amount);
                  
                          _totalSupply -= amount;
                          balances[owner] -= amount;
                          Redeem(amount);
                      }
                  
                      function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
                          // Ensure transparency by hardcoding limit beyond which fees can never be added
                          require(newBasisPoints < 20);
                          require(newMaxFee < 50);
                  
                          basisPointsRate = newBasisPoints;
                          maximumFee = newMaxFee.mul(10**decimals);
                  
                          Params(basisPointsRate, maximumFee);
                      }
                  
                      // Called when new token are issued
                      event Issue(uint amount);
                  
                      // Called when tokens are redeemed
                      event Redeem(uint amount);
                  
                      // Called when contract is deprecated
                      event Deprecate(address newAddress);
                  
                      // Called if contract ever adds fees
                      event Params(uint feeBasisPoints, uint maxFee);
                  }

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

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

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

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