Token

 

Overview

Max Total Supply:
0 N/A

Holders:
0

Transfers:
-

Contract:
0x32688C5f716ecC0D0005B70Bde33D4b88Cae794d0x32688C5f716ecC0D0005B70Bde33D4b88Cae794d

Decimals:
18

Social Profiles:
Not Available, Update ?

 
Loading
[ Download CSV Export  ] 
Loading
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
RewardsController

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 100000 runs

Other Settings:
berlin EvmVersion, BSL 1.1 license
File 1 of 12 : IERC20.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
  /**
   * @dev Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

  /**
   * @dev Returns the amount of tokens owned by `account`.
   */
  function balanceOf(address account) external view returns (uint256);

  /**
   * @dev Moves `amount` tokens from the caller's account to `recipient`.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transfer(address recipient, uint256 amount) external returns (bool);

  /**
   * @dev Returns the remaining number of tokens that `spender` will be
   * allowed to spend on behalf of `owner` through {transferFrom}. This is
   * zero by default.
   *
   * This value changes when {approve} or {transferFrom} are called.
   */
  function allowance(address owner, address spender) external view returns (uint256);

  /**
   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * IMPORTANT: Beware that changing an allowance with this method brings the risk
   * that someone may use both the old and the new allowance by unfortunate
   * transaction ordering. One possible solution to mitigate this race
   * condition is to first reduce the spender's allowance to 0 and set the
   * desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   *
   * Emits an {Approval} event.
   */
  function approve(address spender, uint256 amount) external returns (bool);

  /**
   * @dev Moves `amount` tokens from `sender` to `recipient` using the
   * allowance mechanism. `amount` is then deducted from the caller's
   * allowance.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

  /**
   * @dev Emitted when `value` tokens are moved from one account (`from`) to
   * another (`to`).
   *
   * Note that `value` may be zero.
   */
  event Transfer(address indexed from, address indexed to, uint256 value);

  /**
   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
   * a call to {approve}. `value` is the new allowance.
   */
  event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 2 of 12 : IERC20Detailed.sol
// 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);
}

File 3 of 12 : SafeCast.sol
// 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);
  }
}

File 4 of 12 : IScaledBalanceToken.sol
// 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);
}

File 5 of 12 : VersionedInitializable.sol
// 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;
}

File 6 of 12 : IEACAggregatorProxy.sol
// 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);
}

File 7 of 12 : RewardsController.sol
// 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));
  }
}

File 8 of 12 : RewardsDistributor.sol
// 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;
  }
}

File 9 of 12 : IRewardsController.sol
// 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);
}

File 10 of 12 : IRewardsDistributor.sol
// 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);
}

File 11 of 12 : ITransferStrategyBase.sol
// 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;
}

File 12 of 12 : RewardsDataTypes.sol
// 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;
  }
}

Settings
{
  "evmVersion": "berlin",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"emissionManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardsAccrued","type":"uint256"}],"name":"Accrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldEmission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newEmission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldDistributionEnd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDistributionEnd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetIndex","type":"uint256"}],"name":"AssetConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"ClaimerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"address","name":"rewardOracle","type":"address"}],"name":"RewardOracleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"address","name":"transferStrategy","type":"address"}],"name":"TransferStrategyInstalled","type":"event"},{"inputs":[],"name":"EMISSION_MANAGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REVISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"claimAllRewards","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"claimAllRewardsOnBehalf","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"}],"name":"claimAllRewardsToSelf","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewardsOnBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewardsToSelf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint88","name":"emissionPerSecond","type":"uint88"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint32","name":"distributionEnd","type":"uint32"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"contract ITransferStrategyBase","name":"transferStrategy","type":"address"},{"internalType":"contract IEACAggregatorProxy","name":"rewardOracle","type":"address"}],"internalType":"struct RewardsDataTypes.RewardsConfigInput[]","name":"config","type":"tuple[]"}],"name":"configureAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"}],"name":"getAllUserRewards","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"unclaimedAmounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getAssetDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getAssetIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getClaimer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getDistributionEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEmissionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"}],"name":"getRewardOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getRewardsByAsset","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getRewardsData","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardsList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"}],"name":"getTransferStrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserAccruedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserAssetIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"userBalance","type":"uint256"}],"name":"handleAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"caller","type":"address"}],"name":"setClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"uint32","name":"newDistributionEnd","type":"uint32"}],"name":"setDistributionEnd","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address[]","name":"rewards","type":"address[]"},{"internalType":"uint88[]","name":"newEmissionsPerSecond","type":"uint88[]"}],"name":"setEmissionPerSecond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"},{"internalType":"contract IEACAggregatorProxy","name":"rewardOracle","type":"address"}],"name":"setRewardOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"},{"internalType":"contract ITransferStrategyBase","name":"transferStrategy","type":"address"}],"name":"setTransferStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405260006005553480156200001657600080fd5b506040516200499f3803806200499f83398101604081905262000039916200004b565b6001600160a01b03166080526200007d565b6000602082840312156200005e57600080fd5b81516001600160a01b03811681146200007657600080fd5b9392505050565b6080516148d5620000ca600039600081816104f40152818161060c01528181610c9701528181610fd60152818161167d01528181611833015281816118dc01526119f701526148d56000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c806392074b0811610104578063bf90f63a116100a2578063dde43cba11610071578063dde43cba1461062e578063e15ac62314610636578063f5cf673b14610649578063f996868b1461065c57600080fd5b8063bf90f63a146105ce578063c4d66de8146105e1578063c5a7b538146105f4578063cbcbb5071461060757600080fd5b80639ff55db9116100de5780639ff55db91461058d578063b022418c146105a0578063b45ac1a9146105b3578063bb492bf5146105bb57600080fd5b806392074b08146104f2578063955c2ad7146105185780639efd6f721461052b57600080fd5b80635453ba101161017c57806370674ab91161014b57806370674ab9146103a257806374d945ec146103b55780637eff4ba8146103ee578063886fe70b146104ca57600080fd5b80635453ba101461032357806357b89883146103365780635f130b24146103495780636657732f1461038257600080fd5b806331873e2e116101b857806331873e2e1461027657806333028b991461028b5780634c0369c31461029e578063533f542a146102bf57600080fd5b80631b839c77146101df578063236300dc146102055780632a17bf6014610218575b600080fd5b6101f26101ed366004613e6d565b61066f565b6040519081526020015b60405180910390f35b6101f2610213366004613eeb565b6106cf565b610251610226366004613f5f565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603b60205260409020541690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101fc565b610289610284366004613f83565b61076c565b005b6101f2610299366004613fb8565b61077d565b6102b16102ac36600461403d565b610929565b6040516101fc9291906140e5565b6101f26102cd36600461413c565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260016020818152604080842086861685528252808420948816845293909101905220546cffffffffffffffffffffffffff169392505050565b610289610331366004613e6d565b610c7f565b6101f261034436600461417c565b610d2c565b610251610357366004613f5f565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603a60205260409020541690565b610395610390366004613f5f565b610d46565b6040516101fc91906141db565b6101f26103b03660046141ee565b610e98565b6102516103c3366004613f5f565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603960205260409020541690565b6104aa6103fc366004613e6d565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526001602090815260408083209390941682529190915220546cffffffffffffffffffffffffff8116916affffffffffffffffffffff6d01000000000000000000000000008304169163ffffffff780100000000000000000000000000000000000000000000000082048116927c01000000000000000000000000000000000000000000000000000000009092041690565b6040805194855260208501939093529183015260608201526080016101fc565b6104dd6104d8366004613e6d565b610eaf565b604080519283526020830191909152016101fc565b7f0000000000000000000000000000000000000000000000000000000000000000610251565b610289610526366004614326565b610fbe565b61057b610539366004613f5f565b73ffffffffffffffffffffffffffffffffffffffff16600090815260016020526040902060020154700100000000000000000000000000000000900460ff1690565b60405160ff90911681526020016101fc565b6102b161059b3660046141ee565b6111be565b6101f26105ae366004613e6d565b61136d565b610395611426565b6102b16105c936600461403d565b611495565b6102b16105dc366004614454565b61152e565b6102896105ef366004613f5f565b611549565b610289610602366004614496565b611665565b6102517f000000000000000000000000000000000000000000000000000000000000000081565b6101f2600181565b610289610644366004613e6d565b61181b565b610289610657366004613e6d565b6118c4565b61028961066a3660046144dd565b6119df565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600160209081526040808320938516835292905220547c0100000000000000000000000000000000000000000000000000000000900463ffffffff165b92915050565b600073ffffffffffffffffffffffffffffffffffffffff8316610753576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064015b60405180910390fd5b61076286868633338888611e53565b9695505050505050565b610778338483856120e4565b505050565b73ffffffffffffffffffffffffffffffffffffffff808416600090815260396020526040812054909133918691168214610813576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a4544000000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff8616610890576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f41444452455353000000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff851661090d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f414444524553530000000000000000000000000000604482015260640161074a565b61091c898989338a8a8a611e53565b9998505050505050505050565b6060806000610939868686612297565b60035490915067ffffffffffffffff8111156109575761095761424b565b604051908082528060200260200182016040528015610980578160200160208202803683370190505b509250825167ffffffffffffffff81111561099d5761099d61424b565b6040519080825280602002602001820160405280156109c6578160200160208202803683370190505b50915060005b8151811015610c745760005b8451811015610c6157600381815481106109f4576109f4614560565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16858281518110610a3157610a31614560565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060016000848481518110610a8157610a81614560565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868381518110610ade57610ade614560565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16848281518110610ba457610ba4614560565b60200260200101818151610bb891906145be565b9052508251839083908110610bcf57610bcf614560565b60200260200101516020015160001415610be857610c4f565b610c2586868381518110610bfe57610bfe614560565b6020026020010151858581518110610c1857610c18614560565b6020026020010151612495565b848281518110610c3757610c37614560565b60200260200101818151610c4b91906145be565b9052505b80610c59816145d6565b9150506109d8565b5080610c6c816145d6565b9150506109cc565b50505b935093915050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610d1e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b610d288282612563565b5050565b6000610d3d85858533333388611e53565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600160205260408120600201546060916fffffffffffffffffffffffffffffffff909116908167ffffffffffffffff811115610da057610da061424b565b604051908082528060200260200182016040528015610dc9578160200160208202803683370190505b50905060005b826fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161015610e905773ffffffffffffffffffffffffffffffffffffffff80861660009081526001602081815260408084206fffffffffffffffffffffffffffffffff871680865293019091529091205484519216918491908110610e5957610e59614560565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015280610e888161460f565b915050610dcf565b509392505050565b6000610d3d8383610eaa888888612297565b6126b7565b73ffffffffffffffffffffffffffffffffffffffff8083166000818152600160209081526040808320948616835293815283822084517fb1bf962d0000000000000000000000000000000000000000000000000000000081529451929485949193610fb19385939263b1bf962d92600480830193928290030181865afa158015610f3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f61919061463f565b73ffffffffffffffffffffffffffffffffffffffff8816600090815260016020526040902060020154610fac90700100000000000000000000000000000000900460ff16600a614778565b612856565b92509250505b9250929050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461105d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b60005b81518110156111b15781818151811061107b5761107b614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f5919061463f565b82828151811061110757611107614560565b6020026020010151602001818152505061115b82828151811061112c5761112c614560565b60200260200101516080015183838151811061114a5761114a614560565b602002602001015160a00151612962565b61119f82828151811061117057611170614560565b60200260200101516080015183838151811061118e5761118e614560565b602002602001015160c00151612563565b806111a9816145d6565b915050611060565b506111bb81612ac8565b50565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260396020526040902054606091829133918691168214611257576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a4544000000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff86166112d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f41444452455353000000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff8516611351576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f414444524553530000000000000000000000000000604482015260640161074a565b61135e8888338989613369565b93509350505094509492505050565b60008060005b600454811015610e9057600160006004838154811061139457611394614560565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff908116845283820194909452604092830182208885168352815282822093891682526001909301909252902054611412906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16836145be565b91508061141e816145d6565b915050611373565b6060600380548060200260200160405190810160405280929190818152602001828054801561148b57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611460575b5050505050905090565b60608073ffffffffffffffffffffffffffffffffffffffff8316611515576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f414444524553530000000000000000000000000000604482015260640161074a565b6115228585333387613369565b91509150935093915050565b60608061153e8484333333613369565b915091509250929050565b60065460019060ff168061155c5750303b155b80611568575060055481115b6115f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560448201527f656e20696e697469616c697a6564000000000000000000000000000000000000606482015260840161074a565b60065460ff1615801561163257600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560058290555b801561077857600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611704576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167c010000000000000000000000000000000000000000000000000000000063ffffffff8981168281029384179586905587516d01000000000000000000000000009096046affffffffffffffffffffff16808752968601969096529083041694830185905260608301939093526cffffffffffffffffffffffffff9081169216919091176080820152909291907fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59060a00160405180910390a350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146118ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b610d288282612962565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611963576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff82811660008181526039602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611a7e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b828114611ae7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f494e56414c49445f494e50555400000000000000000000000000000000000000604482015260640161074a565b60005b83811015611e4b5773ffffffffffffffffffffffffffffffffffffffff86166000908152600160205260408120908181888886818110611b2c57611b2c614560565b9050602002016020810190611b419190613f5f565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002830154909150700100000000000000000000000000000000900460ff168015801590611bb7575081547801000000000000000000000000000000000000000000000000900463ffffffff1615155b611c1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f444953545249425554494f4e5f444f45535f4e4f545f45584953540000000000604482015260640161074a565b6000611ca2838b73ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c92919061463f565b611c9d85600a614787565b613851565b5083549091506d010000000000000000000000000090046affffffffffffffffffffff16878787818110611cd857611cd8614560565b9050602002016020810190611ced9190614793565b84546affffffffffffffffffffff919091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff909116178455898987818110611d4857611d48614560565b9050602002016020810190611d5d9190613f5f565b73ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5838b8b8b818110611dbe57611dbe614560565b9050602002016020810190611dd39190614793565b8854604080519384526affffffffffffffffffffff90921660208401527c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690820181905260608201526080810186905260a00160405180910390a350505050508080611e43906145d6565b915050611aea565b505050505050565b600085611e62575060006120d9565b6000611e7885611e738b8b89612297565b6139df565b60005b8881101561205f5760008a8a83818110611e9757611e97614560565b9050602002016020810190611eac9190613f5f565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526001602081815260408084208b861685528252808420948d1684529390910190522054909150611f1c906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16846145be565b9250888311611f8f5773ffffffffffffffffffffffffffffffffffffffff80821660009081526001602081815260408084208a861685528252808420948c1684529390910190522080547fffffff00000000000000000000000000000000ffffffffffffffffffffffffff16905561204c565b6000611f9b8a856147ae565b9050611fa781856147ae565b9350611fb281613a60565b73ffffffffffffffffffffffffffffffffffffffff92831660009081526001602081815260408084208b881685528252808420968d1684529590910190529290922080546fffffffffffffffffffffffffffffffff939093166d0100000000000000000000000000027fffffff00000000000000000000000000000000ffffffffffffffffffffffffff909316929092179091555061205f565b5080612057816145d6565b915050611e7b565b508061206f5760009150506120d9565b61207a848483613b06565b6040805173ffffffffffffffffffffffffffffffffffffffff8881168252602082018490528087169286821692918916917fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f004910160405180910390a490505b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff841660009081526001602052604090206002015460ff700100000000000000000000000000000000820416600a0a906fffffffffffffffffffffffffffffffff1680612146575050612291565b60005b81816fffffffffffffffffffffffffffffffff16101561228d5773ffffffffffffffffffffffffffffffffffffffff80881660009081526001602081815260408084206fffffffffffffffffffffffffffffffff8716855292830182528084205490941680845291905291812090806121c3838989613851565b915091506000806121d7858d8d878d613c32565b9150915082806121e45750805b1561227b578b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167f3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe8878887604051612272939291909283526020830191909152604082015260600190565b60405180910390a45b50506001909401935061214992505050565b5050505b50505050565b60608267ffffffffffffffff8111156122b2576122b261424b565b60405190808252806020026020018201604052801561231d57816020015b61230a6040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b8152602001906001900390816122d05790505b50905060005b83811015610e905784848281811061233d5761233d614560565b90506020020160208101906123529190613f5f565b82828151811061236457612364614560565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff909116905284848281811061239b5761239b614560565b90506020020160208101906123b09190613f5f565b6040517f0afbcdc900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301529190911690630afbcdc9906024016040805180830381865afa15801561241d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061244191906147c5565b83838151811061245357612453614560565b602002602001015160200184848151811061247057612470614560565b602090810291909101015160400191909152528061248d816145d6565b915050612323565b805173ffffffffffffffffffffffffffffffffffffffff90811660009081526001602081815260408084208786168552825280842086519095168452919052812060020154909190829061250190700100000000000000000000000000000000900460ff16600a614778565b9050600061251483866040015184612856565b60208088015173ffffffffffffffffffffffffffffffffffffffff8b166000908152600188019092526040909120549193506120d992509083906cffffffffffffffffffffffffff1685613d91565b60008173ffffffffffffffffffffffffffffffffffffffff166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d4919061463f565b1361263b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f5241434c455f4d5553545f52455455524e5f50524943450000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603b602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f1a1cd5483e52e60b9ff7f3b9d1db3bbd9e9d21c6324ad3a8c79dba9b75e62f4d9190a35050565b6000805b8251811015610e90578281815181106126d6576126d6614560565b60200260200101516020015160001415612785576001600084838151811061270057612700614560565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205461277e906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16836145be565b9150612844565b6001600084838151811061279b5761279b614560565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205483516d01000000000000000000000000009091046fffffffffffffffffffffffffffffffff169061282d9087908790879086908110610c1857610c18614560565b61283791906145be565b61284190836145be565b91505b8061284e816145d6565b9150506126bb565b825460009081906cffffffffffffffffffffffffff81169063ffffffff7c010000000000000000000000000000000000000000000000000000000082048116916affffffffffffffffffffff6d0100000000000000000000000000820416917801000000000000000000000000000000000000000000000000909104168115806128de575087155b806128e857504281145b806128f35750828110155b156129075783849550955050505050610c77565b60008342116129165742612918565b835b9050600061292683836147ae565b905060008961293583876147e9565b61293f91906147e9565b8b900490508661294f81836145be565b9850985050505050505050935093915050565b73ffffffffffffffffffffffffffffffffffffffff81166129df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f53545241544547595f43414e5f4e4f545f42455f5a45524f0000000000000000604482015260640161074a565b6001813b151514612a4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53545241544547595f4d5553545f42455f434f4e545241435400000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f8ca1d928f1d72493a6b78c4f74aabde976bc37ffe2570f2a1ce5a8abd3dde0aa9190a35050565b60005b8151811015610d285760016000838381518110612aea57612aea614560565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002060020154700100000000000000000000000000000000900460ff16612bb6576004828281518110612b5157612b51614560565b6020908102919091018101516060015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b6000828281518110612bca57612bca614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c449190614826565b60016000858581518110612c5a57612c5a614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160106101000a81548160ff021916908360ff160217905560ff169050600060016000858581518110612cd757612cd7614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000858581518110612d3457612d34614560565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002080549091507801000000000000000000000000000000000000000000000000900463ffffffff16612fa357838381518110612da557612da5614560565b60200260200101516080015160016000868681518110612dc757612dc7614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101600060016000888881518110612e2857612e28614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060016000858581518110612f1457612f14614560565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600090812060020180546fffffffffffffffffffffffffffffffff1691612f6b8361460f565b91906101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550505b60026000858581518110612fb957612fb9614560565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff166130e35760016002600086868151811061300d5761300d614560565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600384848151811061307e5761307e614560565b6020908102919091018101516080015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b6000613114828686815181106130fb576130fb614560565b60200260200101516020015185600a611c9d9190614787565b50825486519192506d010000000000000000000000000081046affffffffffffffffffffff16917c010000000000000000000000000000000000000000000000000000000090910463ffffffff169087908790811061317557613175614560565b60209081029190910101515184546affffffffffffffffffffff9091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff90911617845586518790879081106131dd576131dd614560565b602090810291909101015160400151845463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116178455865187908790811061324c5761324c614560565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1687878151811061328057613280614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5848a8a815181106132d6576132d6614560565b602002602001015160000151858c8c815181106132f5576132f5614560565b602002602001015160400151896040516133499594939291906affffffffffffffffffffff958616815293909416602084015263ffffffff9182166040840152166060820152608081019190915260a00190565b60405180910390a350505050508080613361906145d6565b915050612acb565b60035460609081908067ffffffffffffffff81111561338a5761338a61424b565b6040519080825280602002602001820160405280156133b3578160200160208202803683370190505b5092508067ffffffffffffffff8111156133cf576133cf61424b565b6040519080825280602002602001820160405280156133f8578160200160208202803683370190505b50915061340a85611e738a8a89612297565b60005b8781101561371957600089898381811061342957613429614560565b905060200201602081019061343e9190613f5f565b905060005b8381101561370457600073ffffffffffffffffffffffffffffffffffffffff1686828151811061347557613475614560565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16141561352457600381815481106134ac576134ac614560565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168682815181106134e9576134e9614560565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604081208751829089908590811061355f5761355f614560565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683528282019390935260409182016000908120938d168152600190930190529020546d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16905080156136f157808683815181106135e3576135e3614560565b602002602001018181516135f791906145be565b90525073ffffffffffffffffffffffffffffffffffffffff83166000908152600160205260408120885182908a908690811061363557613635614560565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b50806136fc816145d6565b915050613443565b50508080613711906145d6565b91505061340d565b5060005b81811015613845576137628585838151811061373b5761373b614560565b602002602001015185848151811061375557613755614560565b6020026020010151613b06565b8473ffffffffffffffffffffffffffffffffffffffff1684828151811061378b5761378b614560565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a8786815181106137f4576137f4614560565b602002602001015160405161382b92919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b60405180910390a48061383d816145d6565b91505061371d565b50509550959350505050565b600080600080613862878787612856565b91509150600082821461397b576cffffffffffffffffffffffffff8211156138e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e4445585f4f564552464c4f57000000000000000000000000000000000000604482015260640161074a565b5086547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff8216178755600161392942613db5565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788556139d2565b61398442613db5565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788555b9097909650945050505050565b60005b815181101561077857613a4e828281518110613a0057613a00614560565b60200260200101516000015184848481518110613a1f57613a1f614560565b602002602001015160200151858581518110613a3d57613a3d614560565b6020026020010151604001516120e4565b80613a58816145d6565b9150506139e2565b60006fffffffffffffffffffffffffffffffff821115613b02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f3238206269747300000000000000000000000000000000000000000000000000606482015260840161074a565b5090565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a60205260408082205490517f16beb9820000000000000000000000000000000000000000000000000000000081528785166004820152602481019390935260448301859052909216919082906316beb982906064016020604051808303816000875af1158015613b99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bbd9190614849565b9050600181151514613c2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5452414e534645525f4552524f52000000000000000000000000000000000000604482015260640161074a565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260018601602052604081205481906cffffffffffffffffffffffffff1681858214801590613d825773ffffffffffffffffffffffffffffffffffffffff8916600090815260018b016020526040902080547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff89161790558715613d8257613ce688888589613d91565b9150613cf182613a60565b73ffffffffffffffffffffffffffffffffffffffff8a16600090815260018c01602052604090208054600d90613d4b9084906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff1661486b565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b90999098509650505050505050565b600080613d9e84866147ae565b613da890876147e9565b9290920495945050505050565b600063ffffffff821115613b02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f3220626974730000000000000000000000000000000000000000000000000000606482015260840161074a565b73ffffffffffffffffffffffffffffffffffffffff811681146111bb57600080fd5b60008060408385031215613e8057600080fd5b8235613e8b81613e4b565b91506020830135613e9b81613e4b565b809150509250929050565b60008083601f840112613eb857600080fd5b50813567ffffffffffffffff811115613ed057600080fd5b6020830191508360208260051b8501011115610fb757600080fd5b600080600080600060808688031215613f0357600080fd5b853567ffffffffffffffff811115613f1a57600080fd5b613f2688828901613ea6565b909650945050602086013592506040860135613f4181613e4b565b91506060860135613f5181613e4b565b809150509295509295909350565b600060208284031215613f7157600080fd5b8135613f7c81613e4b565b9392505050565b600080600060608486031215613f9857600080fd5b8335613fa381613e4b565b95602085013595506040909401359392505050565b60008060008060008060a08789031215613fd157600080fd5b863567ffffffffffffffff811115613fe857600080fd5b613ff489828a01613ea6565b90975095505060208701359350604087013561400f81613e4b565b9250606087013561401f81613e4b565b9150608087013561402f81613e4b565b809150509295509295509295565b60008060006040848603121561405257600080fd5b833567ffffffffffffffff81111561406957600080fd5b61407586828701613ea6565b909450925050602084013561408981613e4b565b809150509250925092565b600081518084526020808501945080840160005b838110156140da57815173ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016140a8565b509495945050505050565b6040815260006140f86040830185614094565b82810360208481019190915284518083528582019282019060005b8181101561412f57845183529383019391830191600101614113565b5090979650505050505050565b60008060006060848603121561415157600080fd5b833561415c81613e4b565b9250602084013561416c81613e4b565b9150604084013561408981613e4b565b6000806000806060858703121561419257600080fd5b843567ffffffffffffffff8111156141a957600080fd5b6141b587828801613ea6565b9095509350506020850135915060408501356141d081613e4b565b939692955090935050565b602081526000613f7c6020830184614094565b6000806000806060858703121561420457600080fd5b843567ffffffffffffffff81111561421b57600080fd5b61422787828801613ea6565b909550935050602085013561423b81613e4b565b915060408501356141d081613e4b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff8111828210171561429d5761429d61424b565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156142ea576142ea61424b565b604052919050565b80356affffffffffffffffffffff8116811461430d57600080fd5b919050565b803563ffffffff8116811461430d57600080fd5b6000602080838503121561433957600080fd5b823567ffffffffffffffff8082111561435157600080fd5b818501915085601f83011261436557600080fd5b8135818111156143775761437761424b565b614385848260051b016142a3565b818152848101925060e09182028401850191888311156143a457600080fd5b938501935b828510156144485780858a0312156143c15760008081fd5b6143c961427a565b6143d2866142f2565b8152868601358782015260406143e9818801614312565b908201526060868101356143fc81613e4b565b9082015260808681013561440f81613e4b565b9082015260a08681013561442281613e4b565b9082015260c08681013561443581613e4b565b90820152845293840193928501926143a9565b50979650505050505050565b6000806020838503121561446757600080fd5b823567ffffffffffffffff81111561447e57600080fd5b61448a85828601613ea6565b90969095509350505050565b6000806000606084860312156144ab57600080fd5b83356144b681613e4b565b925060208401356144c681613e4b565b91506144d460408501614312565b90509250925092565b6000806000806000606086880312156144f557600080fd5b853561450081613e4b565b9450602086013567ffffffffffffffff8082111561451d57600080fd5b61452989838a01613ea6565b9096509450604088013591508082111561454257600080fd5b5061454f88828901613ea6565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156145d1576145d161458f565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156146085761460861458f565b5060010190565b60006fffffffffffffffffffffffffffffffff808316818114156146355761463561458f565b6001019392505050565b60006020828403121561465157600080fd5b5051919050565b600181815b808511156146b157817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156146975761469761458f565b808516156146a457918102915b93841c939080029061465d565b509250929050565b6000826146c8575060016106c9565b816146d5575060006106c9565b81600181146146eb57600281146146f557614711565b60019150506106c9565b60ff8411156147065761470661458f565b50506001821b6106c9565b5060208310610133831016604e8410600b8410161715614734575081810a6106c9565b61473e8383614658565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156147705761477061458f565b029392505050565b6000613f7c60ff8416836146b9565b6000613f7c83836146b9565b6000602082840312156147a557600080fd5b613f7c826142f2565b6000828210156147c0576147c061458f565b500390565b600080604083850312156147d857600080fd5b505080516020909101519092909150565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156148215761482161458f565b500290565b60006020828403121561483857600080fd5b815160ff81168114613f7c57600080fd5b60006020828403121561485b57600080fd5b81518015158114613f7c57600080fd5b60006fffffffffffffffffffffffffffffffff8083168185168083038211156148965761489661458f565b0194935050505056fea2646970667358221220f618a3b0fc0091036cc335e17b80abf6ffb288b889d7c629df5effe10c0aa22964736f6c634300080a00330000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d14

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101da5760003560e01c806392074b0811610104578063bf90f63a116100a2578063dde43cba11610071578063dde43cba1461062e578063e15ac62314610636578063f5cf673b14610649578063f996868b1461065c57600080fd5b8063bf90f63a146105ce578063c4d66de8146105e1578063c5a7b538146105f4578063cbcbb5071461060757600080fd5b80639ff55db9116100de5780639ff55db91461058d578063b022418c146105a0578063b45ac1a9146105b3578063bb492bf5146105bb57600080fd5b806392074b08146104f2578063955c2ad7146105185780639efd6f721461052b57600080fd5b80635453ba101161017c57806370674ab91161014b57806370674ab9146103a257806374d945ec146103b55780637eff4ba8146103ee578063886fe70b146104ca57600080fd5b80635453ba101461032357806357b89883146103365780635f130b24146103495780636657732f1461038257600080fd5b806331873e2e116101b857806331873e2e1461027657806333028b991461028b5780634c0369c31461029e578063533f542a146102bf57600080fd5b80631b839c77146101df578063236300dc146102055780632a17bf6014610218575b600080fd5b6101f26101ed366004613e6d565b61066f565b6040519081526020015b60405180910390f35b6101f2610213366004613eeb565b6106cf565b610251610226366004613f5f565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603b60205260409020541690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101fc565b610289610284366004613f83565b61076c565b005b6101f2610299366004613fb8565b61077d565b6102b16102ac36600461403d565b610929565b6040516101fc9291906140e5565b6101f26102cd36600461413c565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260016020818152604080842086861685528252808420948816845293909101905220546cffffffffffffffffffffffffff169392505050565b610289610331366004613e6d565b610c7f565b6101f261034436600461417c565b610d2c565b610251610357366004613f5f565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603a60205260409020541690565b610395610390366004613f5f565b610d46565b6040516101fc91906141db565b6101f26103b03660046141ee565b610e98565b6102516103c3366004613f5f565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603960205260409020541690565b6104aa6103fc366004613e6d565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526001602090815260408083209390941682529190915220546cffffffffffffffffffffffffff8116916affffffffffffffffffffff6d01000000000000000000000000008304169163ffffffff780100000000000000000000000000000000000000000000000082048116927c01000000000000000000000000000000000000000000000000000000009092041690565b6040805194855260208501939093529183015260608201526080016101fc565b6104dd6104d8366004613e6d565b610eaf565b604080519283526020830191909152016101fc565b7f0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d14610251565b610289610526366004614326565b610fbe565b61057b610539366004613f5f565b73ffffffffffffffffffffffffffffffffffffffff16600090815260016020526040902060020154700100000000000000000000000000000000900460ff1690565b60405160ff90911681526020016101fc565b6102b161059b3660046141ee565b6111be565b6101f26105ae366004613e6d565b61136d565b610395611426565b6102b16105c936600461403d565b611495565b6102b16105dc366004614454565b61152e565b6102896105ef366004613f5f565b611549565b610289610602366004614496565b611665565b6102517f0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d1481565b6101f2600181565b610289610644366004613e6d565b61181b565b610289610657366004613e6d565b6118c4565b61028961066a3660046144dd565b6119df565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600160209081526040808320938516835292905220547c0100000000000000000000000000000000000000000000000000000000900463ffffffff165b92915050565b600073ffffffffffffffffffffffffffffffffffffffff8316610753576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064015b60405180910390fd5b61076286868633338888611e53565b9695505050505050565b610778338483856120e4565b505050565b73ffffffffffffffffffffffffffffffffffffffff808416600090815260396020526040812054909133918691168214610813576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a4544000000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff8616610890576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f41444452455353000000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff851661090d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f414444524553530000000000000000000000000000604482015260640161074a565b61091c898989338a8a8a611e53565b9998505050505050505050565b6060806000610939868686612297565b60035490915067ffffffffffffffff8111156109575761095761424b565b604051908082528060200260200182016040528015610980578160200160208202803683370190505b509250825167ffffffffffffffff81111561099d5761099d61424b565b6040519080825280602002602001820160405280156109c6578160200160208202803683370190505b50915060005b8151811015610c745760005b8451811015610c6157600381815481106109f4576109f4614560565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16858281518110610a3157610a31614560565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060016000848481518110610a8157610a81614560565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868381518110610ade57610ade614560565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16848281518110610ba457610ba4614560565b60200260200101818151610bb891906145be565b9052508251839083908110610bcf57610bcf614560565b60200260200101516020015160001415610be857610c4f565b610c2586868381518110610bfe57610bfe614560565b6020026020010151858581518110610c1857610c18614560565b6020026020010151612495565b848281518110610c3757610c37614560565b60200260200101818151610c4b91906145be565b9052505b80610c59816145d6565b9150506109d8565b5080610c6c816145d6565b9150506109cc565b50505b935093915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d141614610d1e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b610d288282612563565b5050565b6000610d3d85858533333388611e53565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600160205260408120600201546060916fffffffffffffffffffffffffffffffff909116908167ffffffffffffffff811115610da057610da061424b565b604051908082528060200260200182016040528015610dc9578160200160208202803683370190505b50905060005b826fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161015610e905773ffffffffffffffffffffffffffffffffffffffff80861660009081526001602081815260408084206fffffffffffffffffffffffffffffffff871680865293019091529091205484519216918491908110610e5957610e59614560565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015280610e888161460f565b915050610dcf565b509392505050565b6000610d3d8383610eaa888888612297565b6126b7565b73ffffffffffffffffffffffffffffffffffffffff8083166000818152600160209081526040808320948616835293815283822084517fb1bf962d0000000000000000000000000000000000000000000000000000000081529451929485949193610fb19385939263b1bf962d92600480830193928290030181865afa158015610f3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f61919061463f565b73ffffffffffffffffffffffffffffffffffffffff8816600090815260016020526040902060020154610fac90700100000000000000000000000000000000900460ff16600a614778565b612856565b92509250505b9250929050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d14161461105d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b60005b81518110156111b15781818151811061107b5761107b614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f5919061463f565b82828151811061110757611107614560565b6020026020010151602001818152505061115b82828151811061112c5761112c614560565b60200260200101516080015183838151811061114a5761114a614560565b602002602001015160a00151612962565b61119f82828151811061117057611170614560565b60200260200101516080015183838151811061118e5761118e614560565b602002602001015160c00151612563565b806111a9816145d6565b915050611060565b506111bb81612ac8565b50565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260396020526040902054606091829133918691168214611257576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a4544000000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff86166112d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f41444452455353000000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff8516611351576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f414444524553530000000000000000000000000000604482015260640161074a565b61135e8888338989613369565b93509350505094509492505050565b60008060005b600454811015610e9057600160006004838154811061139457611394614560565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff908116845283820194909452604092830182208885168352815282822093891682526001909301909252902054611412906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16836145be565b91508061141e816145d6565b915050611373565b6060600380548060200260200160405190810160405280929190818152602001828054801561148b57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611460575b5050505050905090565b60608073ffffffffffffffffffffffffffffffffffffffff8316611515576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f414444524553530000000000000000000000000000604482015260640161074a565b6115228585333387613369565b91509150935093915050565b60608061153e8484333333613369565b915091509250929050565b60065460019060ff168061155c5750303b155b80611568575060055481115b6115f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560448201527f656e20696e697469616c697a6564000000000000000000000000000000000000606482015260840161074a565b60065460ff1615801561163257600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560058290555b801561077857600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d141614611704576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167c010000000000000000000000000000000000000000000000000000000063ffffffff8981168281029384179586905587516d01000000000000000000000000009096046affffffffffffffffffffff16808752968601969096529083041694830185905260608301939093526cffffffffffffffffffffffffff9081169216919091176080820152909291907fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59060a00160405180910390a350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d1416146118ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b610d288282612962565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d141614611963576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff82811660008181526039602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d141614611a7e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604482015260640161074a565b828114611ae7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f494e56414c49445f494e50555400000000000000000000000000000000000000604482015260640161074a565b60005b83811015611e4b5773ffffffffffffffffffffffffffffffffffffffff86166000908152600160205260408120908181888886818110611b2c57611b2c614560565b9050602002016020810190611b419190613f5f565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002830154909150700100000000000000000000000000000000900460ff168015801590611bb7575081547801000000000000000000000000000000000000000000000000900463ffffffff1615155b611c1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f444953545249425554494f4e5f444f45535f4e4f545f45584953540000000000604482015260640161074a565b6000611ca2838b73ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c92919061463f565b611c9d85600a614787565b613851565b5083549091506d010000000000000000000000000090046affffffffffffffffffffff16878787818110611cd857611cd8614560565b9050602002016020810190611ced9190614793565b84546affffffffffffffffffffff919091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff909116178455898987818110611d4857611d48614560565b9050602002016020810190611d5d9190613f5f565b73ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5838b8b8b818110611dbe57611dbe614560565b9050602002016020810190611dd39190614793565b8854604080519384526affffffffffffffffffffff90921660208401527c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690820181905260608201526080810186905260a00160405180910390a350505050508080611e43906145d6565b915050611aea565b505050505050565b600085611e62575060006120d9565b6000611e7885611e738b8b89612297565b6139df565b60005b8881101561205f5760008a8a83818110611e9757611e97614560565b9050602002016020810190611eac9190613f5f565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526001602081815260408084208b861685528252808420948d1684529390910190522054909150611f1c906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16846145be565b9250888311611f8f5773ffffffffffffffffffffffffffffffffffffffff80821660009081526001602081815260408084208a861685528252808420948c1684529390910190522080547fffffff00000000000000000000000000000000ffffffffffffffffffffffffff16905561204c565b6000611f9b8a856147ae565b9050611fa781856147ae565b9350611fb281613a60565b73ffffffffffffffffffffffffffffffffffffffff92831660009081526001602081815260408084208b881685528252808420968d1684529590910190529290922080546fffffffffffffffffffffffffffffffff939093166d0100000000000000000000000000027fffffff00000000000000000000000000000000ffffffffffffffffffffffffff909316929092179091555061205f565b5080612057816145d6565b915050611e7b565b508061206f5760009150506120d9565b61207a848483613b06565b6040805173ffffffffffffffffffffffffffffffffffffffff8881168252602082018490528087169286821692918916917fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f004910160405180910390a490505b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff841660009081526001602052604090206002015460ff700100000000000000000000000000000000820416600a0a906fffffffffffffffffffffffffffffffff1680612146575050612291565b60005b81816fffffffffffffffffffffffffffffffff16101561228d5773ffffffffffffffffffffffffffffffffffffffff80881660009081526001602081815260408084206fffffffffffffffffffffffffffffffff8716855292830182528084205490941680845291905291812090806121c3838989613851565b915091506000806121d7858d8d878d613c32565b9150915082806121e45750805b1561227b578b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167f3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe8878887604051612272939291909283526020830191909152604082015260600190565b60405180910390a45b50506001909401935061214992505050565b5050505b50505050565b60608267ffffffffffffffff8111156122b2576122b261424b565b60405190808252806020026020018201604052801561231d57816020015b61230a6040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b8152602001906001900390816122d05790505b50905060005b83811015610e905784848281811061233d5761233d614560565b90506020020160208101906123529190613f5f565b82828151811061236457612364614560565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff909116905284848281811061239b5761239b614560565b90506020020160208101906123b09190613f5f565b6040517f0afbcdc900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301529190911690630afbcdc9906024016040805180830381865afa15801561241d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061244191906147c5565b83838151811061245357612453614560565b602002602001015160200184848151811061247057612470614560565b602090810291909101015160400191909152528061248d816145d6565b915050612323565b805173ffffffffffffffffffffffffffffffffffffffff90811660009081526001602081815260408084208786168552825280842086519095168452919052812060020154909190829061250190700100000000000000000000000000000000900460ff16600a614778565b9050600061251483866040015184612856565b60208088015173ffffffffffffffffffffffffffffffffffffffff8b166000908152600188019092526040909120549193506120d992509083906cffffffffffffffffffffffffff1685613d91565b60008173ffffffffffffffffffffffffffffffffffffffff166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d4919061463f565b1361263b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f5241434c455f4d5553545f52455455524e5f50524943450000000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603b602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f1a1cd5483e52e60b9ff7f3b9d1db3bbd9e9d21c6324ad3a8c79dba9b75e62f4d9190a35050565b6000805b8251811015610e90578281815181106126d6576126d6614560565b60200260200101516020015160001415612785576001600084838151811061270057612700614560565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205461277e906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16836145be565b9150612844565b6001600084838151811061279b5761279b614560565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205483516d01000000000000000000000000009091046fffffffffffffffffffffffffffffffff169061282d9087908790879086908110610c1857610c18614560565b61283791906145be565b61284190836145be565b91505b8061284e816145d6565b9150506126bb565b825460009081906cffffffffffffffffffffffffff81169063ffffffff7c010000000000000000000000000000000000000000000000000000000082048116916affffffffffffffffffffff6d0100000000000000000000000000820416917801000000000000000000000000000000000000000000000000909104168115806128de575087155b806128e857504281145b806128f35750828110155b156129075783849550955050505050610c77565b60008342116129165742612918565b835b9050600061292683836147ae565b905060008961293583876147e9565b61293f91906147e9565b8b900490508661294f81836145be565b9850985050505050505050935093915050565b73ffffffffffffffffffffffffffffffffffffffff81166129df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f53545241544547595f43414e5f4e4f545f42455f5a45524f0000000000000000604482015260640161074a565b6001813b151514612a4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53545241544547595f4d5553545f42455f434f4e545241435400000000000000604482015260640161074a565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f8ca1d928f1d72493a6b78c4f74aabde976bc37ffe2570f2a1ce5a8abd3dde0aa9190a35050565b60005b8151811015610d285760016000838381518110612aea57612aea614560565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002060020154700100000000000000000000000000000000900460ff16612bb6576004828281518110612b5157612b51614560565b6020908102919091018101516060015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b6000828281518110612bca57612bca614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c449190614826565b60016000858581518110612c5a57612c5a614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160106101000a81548160ff021916908360ff160217905560ff169050600060016000858581518110612cd757612cd7614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000858581518110612d3457612d34614560565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002080549091507801000000000000000000000000000000000000000000000000900463ffffffff16612fa357838381518110612da557612da5614560565b60200260200101516080015160016000868681518110612dc757612dc7614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101600060016000888881518110612e2857612e28614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060016000858581518110612f1457612f14614560565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600090812060020180546fffffffffffffffffffffffffffffffff1691612f6b8361460f565b91906101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550505b60026000858581518110612fb957612fb9614560565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff166130e35760016002600086868151811061300d5761300d614560565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600384848151811061307e5761307e614560565b6020908102919091018101516080015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b6000613114828686815181106130fb576130fb614560565b60200260200101516020015185600a611c9d9190614787565b50825486519192506d010000000000000000000000000081046affffffffffffffffffffff16917c010000000000000000000000000000000000000000000000000000000090910463ffffffff169087908790811061317557613175614560565b60209081029190910101515184546affffffffffffffffffffff9091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff90911617845586518790879081106131dd576131dd614560565b602090810291909101015160400151845463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116178455865187908790811061324c5761324c614560565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1687878151811061328057613280614560565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5848a8a815181106132d6576132d6614560565b602002602001015160000151858c8c815181106132f5576132f5614560565b602002602001015160400151896040516133499594939291906affffffffffffffffffffff958616815293909416602084015263ffffffff9182166040840152166060820152608081019190915260a00190565b60405180910390a350505050508080613361906145d6565b915050612acb565b60035460609081908067ffffffffffffffff81111561338a5761338a61424b565b6040519080825280602002602001820160405280156133b3578160200160208202803683370190505b5092508067ffffffffffffffff8111156133cf576133cf61424b565b6040519080825280602002602001820160405280156133f8578160200160208202803683370190505b50915061340a85611e738a8a89612297565b60005b8781101561371957600089898381811061342957613429614560565b905060200201602081019061343e9190613f5f565b905060005b8381101561370457600073ffffffffffffffffffffffffffffffffffffffff1686828151811061347557613475614560565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16141561352457600381815481106134ac576134ac614560565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168682815181106134e9576134e9614560565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604081208751829089908590811061355f5761355f614560565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683528282019390935260409182016000908120938d168152600190930190529020546d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16905080156136f157808683815181106135e3576135e3614560565b602002602001018181516135f791906145be565b90525073ffffffffffffffffffffffffffffffffffffffff83166000908152600160205260408120885182908a908690811061363557613635614560565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b50806136fc816145d6565b915050613443565b50508080613711906145d6565b91505061340d565b5060005b81811015613845576137628585838151811061373b5761373b614560565b602002602001015185848151811061375557613755614560565b6020026020010151613b06565b8473ffffffffffffffffffffffffffffffffffffffff1684828151811061378b5761378b614560565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a8786815181106137f4576137f4614560565b602002602001015160405161382b92919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b60405180910390a48061383d816145d6565b91505061371d565b50509550959350505050565b600080600080613862878787612856565b91509150600082821461397b576cffffffffffffffffffffffffff8211156138e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e4445585f4f564552464c4f57000000000000000000000000000000000000604482015260640161074a565b5086547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff8216178755600161392942613db5565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788556139d2565b61398442613db5565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788555b9097909650945050505050565b60005b815181101561077857613a4e828281518110613a0057613a00614560565b60200260200101516000015184848481518110613a1f57613a1f614560565b602002602001015160200151858581518110613a3d57613a3d614560565b6020026020010151604001516120e4565b80613a58816145d6565b9150506139e2565b60006fffffffffffffffffffffffffffffffff821115613b02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f3238206269747300000000000000000000000000000000000000000000000000606482015260840161074a565b5090565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a60205260408082205490517f16beb9820000000000000000000000000000000000000000000000000000000081528785166004820152602481019390935260448301859052909216919082906316beb982906064016020604051808303816000875af1158015613b99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bbd9190614849565b9050600181151514613c2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5452414e534645525f4552524f52000000000000000000000000000000000000604482015260640161074a565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff8416600090815260018601602052604081205481906cffffffffffffffffffffffffff1681858214801590613d825773ffffffffffffffffffffffffffffffffffffffff8916600090815260018b016020526040902080547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff89161790558715613d8257613ce688888589613d91565b9150613cf182613a60565b73ffffffffffffffffffffffffffffffffffffffff8a16600090815260018c01602052604090208054600d90613d4b9084906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff1661486b565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b90999098509650505050505050565b600080613d9e84866147ae565b613da890876147e9565b9290920495945050505050565b600063ffffffff821115613b02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f3220626974730000000000000000000000000000000000000000000000000000606482015260840161074a565b73ffffffffffffffffffffffffffffffffffffffff811681146111bb57600080fd5b60008060408385031215613e8057600080fd5b8235613e8b81613e4b565b91506020830135613e9b81613e4b565b809150509250929050565b60008083601f840112613eb857600080fd5b50813567ffffffffffffffff811115613ed057600080fd5b6020830191508360208260051b8501011115610fb757600080fd5b600080600080600060808688031215613f0357600080fd5b853567ffffffffffffffff811115613f1a57600080fd5b613f2688828901613ea6565b909650945050602086013592506040860135613f4181613e4b565b91506060860135613f5181613e4b565b809150509295509295909350565b600060208284031215613f7157600080fd5b8135613f7c81613e4b565b9392505050565b600080600060608486031215613f9857600080fd5b8335613fa381613e4b565b95602085013595506040909401359392505050565b60008060008060008060a08789031215613fd157600080fd5b863567ffffffffffffffff811115613fe857600080fd5b613ff489828a01613ea6565b90975095505060208701359350604087013561400f81613e4b565b9250606087013561401f81613e4b565b9150608087013561402f81613e4b565b809150509295509295509295565b60008060006040848603121561405257600080fd5b833567ffffffffffffffff81111561406957600080fd5b61407586828701613ea6565b909450925050602084013561408981613e4b565b809150509250925092565b600081518084526020808501945080840160005b838110156140da57815173ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016140a8565b509495945050505050565b6040815260006140f86040830185614094565b82810360208481019190915284518083528582019282019060005b8181101561412f57845183529383019391830191600101614113565b5090979650505050505050565b60008060006060848603121561415157600080fd5b833561415c81613e4b565b9250602084013561416c81613e4b565b9150604084013561408981613e4b565b6000806000806060858703121561419257600080fd5b843567ffffffffffffffff8111156141a957600080fd5b6141b587828801613ea6565b9095509350506020850135915060408501356141d081613e4b565b939692955090935050565b602081526000613f7c6020830184614094565b6000806000806060858703121561420457600080fd5b843567ffffffffffffffff81111561421b57600080fd5b61422787828801613ea6565b909550935050602085013561423b81613e4b565b915060408501356141d081613e4b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff8111828210171561429d5761429d61424b565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156142ea576142ea61424b565b604052919050565b80356affffffffffffffffffffff8116811461430d57600080fd5b919050565b803563ffffffff8116811461430d57600080fd5b6000602080838503121561433957600080fd5b823567ffffffffffffffff8082111561435157600080fd5b818501915085601f83011261436557600080fd5b8135818111156143775761437761424b565b614385848260051b016142a3565b818152848101925060e09182028401850191888311156143a457600080fd5b938501935b828510156144485780858a0312156143c15760008081fd5b6143c961427a565b6143d2866142f2565b8152868601358782015260406143e9818801614312565b908201526060868101356143fc81613e4b565b9082015260808681013561440f81613e4b565b9082015260a08681013561442281613e4b565b9082015260c08681013561443581613e4b565b90820152845293840193928501926143a9565b50979650505050505050565b6000806020838503121561446757600080fd5b823567ffffffffffffffff81111561447e57600080fd5b61448a85828601613ea6565b90969095509350505050565b6000806000606084860312156144ab57600080fd5b83356144b681613e4b565b925060208401356144c681613e4b565b91506144d460408501614312565b90509250925092565b6000806000806000606086880312156144f557600080fd5b853561450081613e4b565b9450602086013567ffffffffffffffff8082111561451d57600080fd5b61452989838a01613ea6565b9096509450604088013591508082111561454257600080fd5b5061454f88828901613ea6565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156145d1576145d161458f565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156146085761460861458f565b5060010190565b60006fffffffffffffffffffffffffffffffff808316818114156146355761463561458f565b6001019392505050565b60006020828403121561465157600080fd5b5051919050565b600181815b808511156146b157817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156146975761469761458f565b808516156146a457918102915b93841c939080029061465d565b509250929050565b6000826146c8575060016106c9565b816146d5575060006106c9565b81600181146146eb57600281146146f557614711565b60019150506106c9565b60ff8411156147065761470661458f565b50506001821b6106c9565b5060208310610133831016604e8410600b8410161715614734575081810a6106c9565b61473e8383614658565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156147705761477061458f565b029392505050565b6000613f7c60ff8416836146b9565b6000613f7c83836146b9565b6000602082840312156147a557600080fd5b613f7c826142f2565b6000828210156147c0576147c061458f565b500390565b600080604083850312156147d857600080fd5b505080516020909101519092909150565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156148215761482161458f565b500290565b60006020828403121561483857600080fd5b815160ff81168114613f7c57600080fd5b60006020828403121561485b57600080fd5b81518015158114613f7c57600080fd5b60006fffffffffffffffffffffffffffffffff8083168185168083038211156148965761489661458f565b0194935050505056fea2646970667358221220f618a3b0fc0091036cc335e17b80abf6ffb288b889d7c629df5effe10c0aa22964736f6c634300080a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d14

-----Decoded View---------------
Arg [0] : emissionManager (address): 0x6e081F9ebb2B2f07C2f771074EBB32dDac141d14

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000006e081f9ebb2b2f07c2f771074ebb32ddac141d14


Loading