ETH Price: $1,977.41 (+0.18%)

Transaction Decoder

Block:
24475719 at Feb-17-2026 09:41:11 AM +UTC
Transaction Fee:
0.000007424659815318 ETH $0.01
Gas Used:
100,587 Gas / 0.073813314 Gwei

Emitted Events:

492 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000f1c354de3ec63cd7232735d2762f7b0b45ccfeb1, 000000000000000000000000000000000000000000000000000a51adc0242882 )
493 InitializableImmutableAdminUpgradeabilityProxy.0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196( 0x458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b86196, 0x000000000000000000000000f1c354de3ec63cd7232735d2762f7b0b45ccfeb1, 0x000000000000000000000000f1c354de3ec63cd7232735d2762f7b0b45ccfeb1, 000000000000000000000000000000000000000000000000000a51adc0242882, 000000000000000000000000000000000000000000000000000a51adc0242882, 000000000000000000000000000000000000000003b8b6819cac5d049c7d7a82 )
494 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000f1c354de3ec63cd7232735d2762f7b0b45ccfeb1, 0x000000000000000000000000113fa1321c66a5dd50b76fb6eb55630f2446ec78, 0000000000000000000000000000000000000000000003635c9adc5dea000000 )
495 InitializableImmutableAdminUpgradeabilityProxy.0x4beccb90f994c31aced7a23b5611020728a23d8ec5cddd1a3e9d97b96fda8666( 0x4beccb90f994c31aced7a23b5611020728a23d8ec5cddd1a3e9d97b96fda8666, 0x000000000000000000000000f1c354de3ec63cd7232735d2762f7b0b45ccfeb1, 0x000000000000000000000000113fa1321c66a5dd50b76fb6eb55630f2446ec78, 0000000000000000000000000000000000000000000002f11358025627716d80, 000000000000000000000000000000000000000003b8b6819cac5d049c7d7a82 )

Account State Difference:

  Address   Before After State Difference Code
0x018008bf...54f754e63
(BuilderNet)
95.067636532245528907 Eth95.067640141294616119 Eth0.000003609049087212
0xF1C354de...b45cCFeb1
0.036172172357300762 Eth
Nonce: 45
0.036164747697485444 Eth
Nonce: 46
0.000007424659815318

Execution Trace

InitializableImmutableAdminUpgradeabilityProxy.a9059cbb( )
  • 0xadc45df3cf1584624c97338bef33363bf5b97ada.a9059cbb( )
    • InitializableImmutableAdminUpgradeabilityProxy.d15e0053( )
      • 0x8147b99df7672a21809c9093e6f6ce1a60f119bd.d15e0053( )
      • InitializableImmutableAdminUpgradeabilityProxy.31873e2e( )
        • RewardsController.handleAction( user=0xF1C354de3ec63cd7232735d2762F7b0b45cCFeb1, totalSupply=135961387825661556371564080, userBalance=13892171979331390548442 )
        • InitializableImmutableAdminUpgradeabilityProxy.31873e2e( )
          • RewardsController.handleAction( user=0x113fa1321c66a5Dd50B76Fb6eB55630F2446eC78, totalSupply=135961387825661556371564080, userBalance=0 )
          • InitializableImmutableAdminUpgradeabilityProxy.12772993( )
            • 0x8147b99df7672a21809c9093e6f6ce1a60f119bd.12772993( )
              • PoolAddressesProvider.STATICCALL( )
              • 0x33654b16a4de97bce05d7dd06803bf1066f3123c.8a5dadd1( )
                File 1 of 5: 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 5: 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 5: 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 5: 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 5 of 5: 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();
                  }
                }