ETH Price: $3,492.87 (+0.05%)
Gas: 0.13 GWei

Contract

0xDAaa6B18E61bC034a3dfa2280051218C5Eb7d7f8

Overview

ETH Balance

Linea Mainnet LogoLinea Mainnet LogoLinea Mainnet Logo0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
88753782024-09-01 4:23:23115 days ago1725164603
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753712024-09-01 4:23:09115 days ago1725164589
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753662024-09-01 4:22:59115 days ago1725164579
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753622024-09-01 4:22:51115 days ago1725164571
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753412024-09-01 4:22:09115 days ago1725164529
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753302024-09-01 4:21:47115 days ago1725164507
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753282024-09-01 4:21:43115 days ago1725164503
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753282024-09-01 4:21:43115 days ago1725164503
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753222024-09-01 4:21:31115 days ago1725164491
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753172024-09-01 4:21:21115 days ago1725164481
0xDAaa6B18...C5Eb7d7f8
0 ETH
88753042024-09-01 4:20:55115 days ago1725164455
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752882024-09-01 4:20:23115 days ago1725164423
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752832024-09-01 4:20:13115 days ago1725164413
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752662024-09-01 4:19:39115 days ago1725164379
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752662024-09-01 4:19:39115 days ago1725164379
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752662024-09-01 4:19:39115 days ago1725164379
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752612024-09-01 4:19:29115 days ago1725164369
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752612024-09-01 4:19:29115 days ago1725164369
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752612024-09-01 4:19:29115 days ago1725164369
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752542024-09-01 4:19:15115 days ago1725164355
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752522024-09-01 4:19:11115 days ago1725164351
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752522024-09-01 4:19:11115 days ago1725164351
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752522024-09-01 4:19:11115 days ago1725164351
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752122024-09-01 4:17:51115 days ago1725164271
0xDAaa6B18...C5Eb7d7f8
0 ETH
88752002024-09-01 4:17:27115 days ago1725164247
0xDAaa6B18...C5Eb7d7f8
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RewardsController

Compiler Version
v0.8.12+commit.f00d7308

Optimization Enabled:
Yes with 100000 runs

Other Settings:
default evmVersion
File 1 of 13 : RewardsController.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {VersionedInitializable} from '@zerolendxyz/core-v3/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol';
import {SafeCast} from '@zerolendxyz/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol';
import {IScaledBalanceToken} from '@zerolendxyz/core-v3/contracts/interfaces/IScaledBalanceToken.sol';
import {RewardsDistributor} from './RewardsDistributor.sol';
import {IRewardsController} from './interfaces/IRewardsController.sol';
import {ITransferStrategyBase} from './interfaces/ITransferStrategyBase.sol';
import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
import {IEACAggregatorProxy} from '../misc/interfaces/IEACAggregatorProxy.sol';

/**
 * @title RewardsController
 * @notice Abstract contract template to build Distributors contracts for ERC20 rewards to protocol participants
 * @author Aave
 **/
contract RewardsController is RewardsDistributor, VersionedInitializable, IRewardsController {
  using SafeCast for uint256;

  uint256 public constant REVISION = 3;

  // 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,
    address _staking
  ) RewardsDistributor(_emissionManager, _staking) {}

  /**
   * @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 2 of 13 : 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 3 of 13 : IERC20Detailed.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.12;

import {IERC20} from './IERC20.sol';

interface IERC20Detailed is IERC20 {
  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function decimals() external view returns (uint8);
}

File 4 of 13 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
pragma solidity 0.8.12;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
  /**
   * @dev Returns the downcasted uint224 from uint256, reverting on
   * overflow (when the input is greater than largest uint224).
   *
   * Counterpart to Solidity's `uint224` operator.
   *
   * Requirements:
   *
   * - input must fit into 224 bits
   */
  function toUint224(uint256 value) internal pure returns (uint224) {
    require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
    return uint224(value);
  }

  /**
   * @dev Returns the downcasted uint128 from uint256, reverting on
   * overflow (when the input is greater than largest uint128).
   *
   * Counterpart to Solidity's `uint128` operator.
   *
   * Requirements:
   *
   * - input must fit into 128 bits
   */
  function toUint128(uint256 value) internal pure returns (uint128) {
    require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
    return uint128(value);
  }

  /**
   * @dev Returns the downcasted uint96 from uint256, reverting on
   * overflow (when the input is greater than largest uint96).
   *
   * Counterpart to Solidity's `uint96` operator.
   *
   * Requirements:
   *
   * - input must fit into 96 bits
   */
  function toUint96(uint256 value) internal pure returns (uint96) {
    require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
    return uint96(value);
  }

  /**
   * @dev Returns the downcasted uint64 from uint256, reverting on
   * overflow (when the input is greater than largest uint64).
   *
   * Counterpart to Solidity's `uint64` operator.
   *
   * Requirements:
   *
   * - input must fit into 64 bits
   */
  function toUint64(uint256 value) internal pure returns (uint64) {
    require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
    return uint64(value);
  }

  /**
   * @dev Returns the downcasted uint32 from uint256, reverting on
   * overflow (when the input is greater than largest uint32).
   *
   * Counterpart to Solidity's `uint32` operator.
   *
   * Requirements:
   *
   * - input must fit into 32 bits
   */
  function toUint32(uint256 value) internal pure returns (uint32) {
    require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
    return uint32(value);
  }

  /**
   * @dev Returns the downcasted uint16 from uint256, reverting on
   * overflow (when the input is greater than largest uint16).
   *
   * Counterpart to Solidity's `uint16` operator.
   *
   * Requirements:
   *
   * - input must fit into 16 bits
   */
  function toUint16(uint256 value) internal pure returns (uint16) {
    require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
    return uint16(value);
  }

  /**
   * @dev Returns the downcasted uint8 from uint256, reverting on
   * overflow (when the input is greater than largest uint8).
   *
   * Counterpart to Solidity's `uint8` operator.
   *
   * Requirements:
   *
   * - input must fit into 8 bits.
   */
  function toUint8(uint256 value) internal pure returns (uint8) {
    require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
    return uint8(value);
  }

  /**
   * @dev Converts a signed int256 into an unsigned uint256.
   *
   * Requirements:
   *
   * - input must be greater than or equal to 0.
   */
  function toUint256(int256 value) internal pure returns (uint256) {
    require(value >= 0, 'SafeCast: value must be positive');
    return uint256(value);
  }

  /**
   * @dev Returns the downcasted int128 from int256, reverting on
   * overflow (when the input is less than smallest int128 or
   * greater than largest int128).
   *
   * Counterpart to Solidity's `int128` operator.
   *
   * Requirements:
   *
   * - input must fit into 128 bits
   *
   * _Available since v3.1._
   */
  function toInt128(int256 value) internal pure returns (int128) {
    require(
      value >= type(int128).min && value <= type(int128).max,
      "SafeCast: value doesn't fit in 128 bits"
    );
    return int128(value);
  }

  /**
   * @dev Returns the downcasted int64 from int256, reverting on
   * overflow (when the input is less than smallest int64 or
   * greater than largest int64).
   *
   * Counterpart to Solidity's `int64` operator.
   *
   * Requirements:
   *
   * - input must fit into 64 bits
   *
   * _Available since v3.1._
   */
  function toInt64(int256 value) internal pure returns (int64) {
    require(
      value >= type(int64).min && value <= type(int64).max,
      "SafeCast: value doesn't fit in 64 bits"
    );
    return int64(value);
  }

  /**
   * @dev Returns the downcasted int32 from int256, reverting on
   * overflow (when the input is less than smallest int32 or
   * greater than largest int32).
   *
   * Counterpart to Solidity's `int32` operator.
   *
   * Requirements:
   *
   * - input must fit into 32 bits
   *
   * _Available since v3.1._
   */
  function toInt32(int256 value) internal pure returns (int32) {
    require(
      value >= type(int32).min && value <= type(int32).max,
      "SafeCast: value doesn't fit in 32 bits"
    );
    return int32(value);
  }

  /**
   * @dev Returns the downcasted int16 from int256, reverting on
   * overflow (when the input is less than smallest int16 or
   * greater than largest int16).
   *
   * Counterpart to Solidity's `int16` operator.
   *
   * Requirements:
   *
   * - input must fit into 16 bits
   *
   * _Available since v3.1._
   */
  function toInt16(int256 value) internal pure returns (int16) {
    require(
      value >= type(int16).min && value <= type(int16).max,
      "SafeCast: value doesn't fit in 16 bits"
    );
    return int16(value);
  }

  /**
   * @dev Returns the downcasted int8 from int256, reverting on
   * overflow (when the input is less than smallest int8 or
   * greater than largest int8).
   *
   * Counterpart to Solidity's `int8` operator.
   *
   * Requirements:
   *
   * - input must fit into 8 bits.
   *
   * _Available since v3.1._
   */
  function toInt8(int256 value) internal pure returns (int8) {
    require(
      value >= type(int8).min && value <= type(int8).max,
      "SafeCast: value doesn't fit in 8 bits"
    );
    return int8(value);
  }

  /**
   * @dev Converts an unsigned uint256 into a signed int256.
   *
   * Requirements:
   *
   * - input must be less than or equal to maxInt256.
   */
  function toInt256(uint256 value) internal pure returns (int256) {
    // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
    require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
    return int256(value);
  }
}

File 5 of 13 : 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 6 of 13 : VersionedInitializable.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.12;

/**
 * @title VersionedInitializable
 * @author Aave, inspired by the OpenZeppelin Initializable contract
 * @notice Helper contract to implement initializer functions. To use it, replace
 * the constructor with a function that has the `initializer` modifier.
 * @dev WARNING: Unlike constructors, initializer functions must be manually
 * invoked. This applies both to deploying an Initializable contract, as well
 * as extending an Initializable contract via inheritance.
 * WARNING: When used with inheritance, manual care must be taken to not invoke
 * a parent initializer twice, or ensure that all initializers are idempotent,
 * because this is not dealt with automatically as with constructors.
 */
abstract contract VersionedInitializable {
  /**
   * @dev Indicates that the contract has been initialized.
   */
  uint256 private lastInitializedRevision = 0;

  /**
   * @dev Indicates that the contract is in the process of being initialized.
   */
  bool private initializing;

  /**
   * @dev Modifier to use in the initializer function of a contract.
   */
  modifier initializer() {
    uint256 revision = getRevision();
    require(
      initializing || isConstructor() || revision > lastInitializedRevision,
      'Contract instance has already been initialized'
    );

    bool isTopLevelCall = !initializing;
    if (isTopLevelCall) {
      initializing = true;
      lastInitializedRevision = revision;
    }

    _;

    if (isTopLevelCall) {
      initializing = false;
    }
  }

  /**
   * @notice Returns the revision number of the contract
   * @dev Needs to be defined in the inherited class as a constant.
   * @return The revision number
   */
  function getRevision() internal pure virtual returns (uint256);

  /**
   * @notice Returns true if and only if the function is running in the constructor
   * @return True if the function is running in the constructor
   */
  function isConstructor() private view returns (bool) {
    // extcodesize checks the size of the code stored in an address, and
    // address returns the current address. Since the code is still not
    // deployed when running a constructor, any checks on its code size will
    // yield zero, making it an effective way to detect if a contract is
    // under construction or not.
    uint256 cs;
    //solium-disable-next-line
    assembly {
      cs := extcodesize(address())
    }
    return cs == 0;
  }

  // Reserved storage space to allow for layout changes in the future.
  uint256[50] private ______gap;
}

File 7 of 13 : IVotes.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.12;

/**
 * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
 */
interface IVotes {
  /**
   * @dev The signature used has expired.
   */
  error VotesExpiredSignature(uint256 expiry);

  /**
   * @dev Emitted when an account changes their delegate.
   */
  event DelegateChanged(
    address indexed delegator,
    address indexed fromDelegate,
    address indexed toDelegate
  );

  /**
   * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of voting units.
   */
  event DelegateVotesChanged(address indexed delegate, uint256 previousVotes, uint256 newVotes);

  /**
   * @dev Returns the current amount of votes that `account` has.
   */
  function getVotes(address account) external view returns (uint256);

  /**
   * @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
   * configured to use block numbers, this will return the value at the end of the corresponding block.
   */
  function getPastVotes(address account, uint256 timepoint) external view returns (uint256);

  /**
   * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
   * configured to use block numbers, this will return the value at the end of the corresponding block.
   *
   * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
   * Votes that have not been delegated are still part of total supply, even though they would not participate in a
   * vote.
   */
  function getPastTotalSupply(uint256 timepoint) external view returns (uint256);

  /**
   * @dev Returns the delegate that `account` has chosen.
   */
  function delegates(address account) external view returns (address);

  /**
   * @dev Delegates votes from the sender to `delegatee`.
   */
  function delegate(address delegatee) external;

  /**
   * @dev Delegates votes from signer to `delegatee`.
   */
  function delegateBySig(
    address delegatee,
    uint256 nonce,
    uint256 expiry,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;
}

File 8 of 13 : IEACAggregatorProxy.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.12;

interface IEACAggregatorProxy {
  function decimals() external view returns (uint8);

  function latestAnswer() external view returns (int256);

  function latestTimestamp() external view returns (uint256);

  function latestRound() external view returns (uint256);

  function getAnswer(uint256 roundId) external view returns (int256);

  function getTimestamp(uint256 roundId) external view returns (uint256);

  event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
  event NewRound(uint256 indexed roundId, address indexed startedBy);
}

File 9 of 13 : IRewardsController.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.12;

import {IRewardsDistributor} from './IRewardsDistributor.sol';
import {ITransferStrategyBase} from './ITransferStrategyBase.sol';
import {IEACAggregatorProxy} from '../../misc/interfaces/IEACAggregatorProxy.sol';
import {RewardsDataTypes} from '../libraries/RewardsDataTypes.sol';

/**
 * @title IRewardsController
 * @author Aave
 * @notice Defines the basic interface for a Rewards Controller.
 */
interface IRewardsController is IRewardsDistributor {
  /**
   * @dev Emitted when a new address is whitelisted as claimer of rewards on behalf of a user
   * @param user The address of the user
   * @param claimer The address of the claimer
   */
  event ClaimerSet(address indexed user, address indexed claimer);

  /**
   * @dev Emitted when rewards are claimed
   * @param user The address of the user rewards has been claimed on behalf of
   * @param reward The address of the token reward is claimed
   * @param to The address of the receiver of the rewards
   * @param claimer The address of the claimer
   * @param amount The amount of rewards claimed
   */
  event RewardsClaimed(
    address indexed user,
    address indexed reward,
    address indexed to,
    address claimer,
    uint256 amount
  );

  /**
   * @dev Emitted when a transfer strategy is installed for the reward distribution
   * @param reward The address of the token reward
   * @param transferStrategy The address of TransferStrategy contract
   */
  event TransferStrategyInstalled(address indexed reward, address indexed transferStrategy);

  /**
   * @dev Emitted when the reward oracle is updated
   * @param reward The address of the token reward
   * @param rewardOracle The address of oracle
   */
  event RewardOracleUpdated(address indexed reward, address indexed rewardOracle);

  /**
   * @dev Whitelists an address to claim the rewards on behalf of another address
   * @param user The address of the user
   * @param claimer The address of the claimer
   */
  function setClaimer(address user, address claimer) external;

  /**
   * @dev Sets a TransferStrategy logic contract that determines the logic of the rewards transfer
   * @param reward The address of the reward token
   * @param transferStrategy The address of the TransferStrategy logic contract
   */
  function setTransferStrategy(address reward, ITransferStrategyBase transferStrategy) external;

  /**
   * @dev Sets an Aave Oracle contract to enforce rewards with a source of value.
   * @notice At the moment of reward configuration, the Incentives Controller performs
   * a check to see if the reward asset oracle is compatible with IEACAggregator proxy.
   * This check is enforced for integrators to be able to show incentives at
   * the current Aave UI without the need to setup an external price registry
   * @param reward The address of the reward to set the price aggregator
   * @param rewardOracle The address of price aggregator that follows IEACAggregatorProxy interface
   */
  function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) external;

  /**
   * @dev Get the price aggregator oracle address
   * @param reward The address of the reward
   * @return The price oracle of the reward
   */
  function getRewardOracle(address reward) external view returns (address);

  /**
   * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)
   * @param user The address of the user
   * @return The claimer address
   */
  function getClaimer(address user) external view returns (address);

  /**
   * @dev Returns the Transfer Strategy implementation contract address being used for a reward address
   * @param reward The address of the reward
   * @return The address of the TransferStrategy contract
   */
  function getTransferStrategy(address reward) external view returns (address);

  /**
   * @dev Configure assets to incentivize with an emission of rewards per second until the end of distribution.
   * @param config The assets configuration input, the list of structs contains the following fields:
   *   uint104 emissionPerSecond: The emission per second following rewards unit decimals.
   *   uint256 totalSupply: The total supply of the asset to incentivize
   *   uint40 distributionEnd: The end of the distribution of the incentives for an asset
   *   address asset: The asset address to incentivize
   *   address reward: The reward token address
   *   ITransferStrategy transferStrategy: The TransferStrategy address with the install hook and claim logic.
   *   IEACAggregatorProxy rewardOracle: The Price Oracle of a reward to visualize the incentives at the UI Frontend.
   *                                     Must follow Chainlink Aggregator IEACAggregatorProxy interface to be compatible.
   */
  function configureAssets(RewardsDataTypes.RewardsConfigInput[] memory config) external;

  /**
   * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
   * @dev The units of `totalSupply` and `userBalance` should be the same.
   * @param user The address of the user whose asset balance has changed
   * @param totalSupply The total supply of the asset prior to user balance change
   * @param userBalance The previous user balance prior to balance change
   **/
  function handleAction(address user, uint256 totalSupply, uint256 userBalance) external;

  /**
   * @dev Claims reward for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
   * @param assets List of assets to check eligible distributions before claiming rewards
   * @param amount The amount of rewards to claim
   * @param to The address that will be receiving the rewards
   * @param reward The address of the reward token
   * @return The amount of rewards claimed
   **/
  function claimRewards(
    address[] calldata assets,
    uint256 amount,
    address to,
    address reward
  ) external returns (uint256);

  /**
   * @dev Claims reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The
   * caller must be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @param amount The amount of rewards to claim
   * @param user The address to check and claim rewards
   * @param to The address that will be receiving the rewards
   * @param reward The address of the reward token
   * @return The amount of rewards claimed
   **/
  function claimRewardsOnBehalf(
    address[] calldata assets,
    uint256 amount,
    address user,
    address to,
    address reward
  ) external returns (uint256);

  /**
   * @dev Claims reward for msg.sender, on all the assets of the pool, accumulating the pending rewards
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @param amount The amount of rewards to claim
   * @param reward The address of the reward token
   * @return The amount of rewards claimed
   **/
  function claimRewardsToSelf(
    address[] calldata assets,
    uint256 amount,
    address reward
  ) external returns (uint256);

  /**
   * @dev Claims all rewards for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @param to The address that will be receiving the rewards
   * @return rewardsList List of addresses of the reward tokens
   * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardList"
   **/
  function claimAllRewards(
    address[] calldata assets,
    address to
  ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts);

  /**
   * @dev Claims all rewards for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The caller must
   * be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @param user The address to check and claim rewards
   * @param to The address that will be receiving the rewards
   * @return rewardsList List of addresses of the reward tokens
   * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList"
   **/
  function claimAllRewardsOnBehalf(
    address[] calldata assets,
    address user,
    address to
  ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts);

  /**
   * @dev Claims all reward for msg.sender, on all the assets of the pool, accumulating the pending rewards
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @return rewardsList List of addresses of the reward tokens
   * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList"
   **/
  function claimAllRewardsToSelf(
    address[] calldata assets
  ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
}

File 10 of 13 : IRewardsDistributor.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.12;

/**
 * @title IRewardsDistributor
 * @author Aave
 * @notice Defines the basic interface for a Rewards Distributor.
 */
interface IRewardsDistributor {
  /**
   * @dev Emitted when the configuration of the rewards of an asset is updated.
   * @param asset The address of the incentivized asset
   * @param reward The address of the reward token
   * @param oldEmission The old emissions per second value of the reward distribution
   * @param newEmission The new emissions per second value of the reward distribution
   * @param oldDistributionEnd The old end timestamp of the reward distribution
   * @param newDistributionEnd The new end timestamp of the reward distribution
   * @param assetIndex The index of the asset distribution
   */
  event AssetConfigUpdated(
    address indexed asset,
    address indexed reward,
    uint256 oldEmission,
    uint256 newEmission,
    uint256 oldDistributionEnd,
    uint256 newDistributionEnd,
    uint256 assetIndex
  );

  /**
   * @dev Emitted when rewards of an asset are accrued on behalf of a user.
   * @param asset The address of the incentivized asset
   * @param reward The address of the reward token
   * @param user The address of the user that rewards are accrued on behalf of
   * @param assetIndex The index of the asset distribution
   * @param userIndex The index of the asset distribution on behalf of the user
   * @param rewardsAccrued The amount of rewards accrued
   */
  event Accrued(
    address indexed asset,
    address indexed reward,
    address indexed user,
    uint256 assetIndex,
    uint256 userIndex,
    uint256 rewardsAccrued
  );

  /**
   * @dev Sets the end date for the distribution
   * @param asset The asset to incentivize
   * @param reward The reward token that incentives the asset
   * @param newDistributionEnd The end date of the incentivization, in unix time format
   **/
  function setDistributionEnd(address asset, address reward, uint32 newDistributionEnd) external;

  /**
   * @dev Sets the emission per second of a set of reward distributions
   * @param asset The asset is being incentivized
   * @param rewards List of reward addresses are being distributed
   * @param newEmissionsPerSecond List of new reward emissions per second
   */
  function setEmissionPerSecond(
    address asset,
    address[] calldata rewards,
    uint88[] calldata newEmissionsPerSecond
  ) external;

  /**
   * @dev Gets the end date for the distribution
   * @param asset The incentivized asset
   * @param reward The reward token of the incentivized asset
   * @return The timestamp with the end of the distribution, in unix time format
   **/
  function getDistributionEnd(address asset, address reward) external view returns (uint256);

  /**
   * @dev Returns the index of a user on a reward distribution
   * @param user Address of the user
   * @param asset The incentivized asset
   * @param reward The reward token of the incentivized asset
   * @return The current user asset index, not including new distributions
   **/
  function getUserAssetIndex(
    address user,
    address asset,
    address reward
  ) external view returns (uint256);

  /**
   * @dev Returns the configuration of the distribution reward for a certain asset
   * @param asset The incentivized asset
   * @param reward The reward token of the incentivized asset
   * @return The index of the asset distribution
   * @return The emission per second of the reward distribution
   * @return The timestamp of the last update of the index
   * @return The timestamp of the distribution end
   **/
  function getRewardsData(
    address asset,
    address reward
  ) external view returns (uint256, uint256, uint256, uint256);

  /**
   * @dev Calculates the next value of an specific distribution index, with validations.
   * @param asset The incentivized asset
   * @param reward The reward token of the incentivized asset
   * @return The old index of the asset distribution
   * @return The new index of the asset distribution
   **/
  function getAssetIndex(address asset, address reward) external view returns (uint256, uint256);

  /**
   * @dev Returns the list of available reward token addresses of an incentivized asset
   * @param asset The incentivized asset
   * @return List of rewards addresses of the input asset
   **/
  function getRewardsByAsset(address asset) external view returns (address[] memory);

  /**
   * @dev Returns the list of available reward addresses
   * @return List of rewards supported in this contract
   **/
  function getRewardsList() external view returns (address[] memory);

  /**
   * @dev Returns the accrued rewards balance of a user, not including virtually accrued rewards since last distribution.
   * @param user The address of the user
   * @param reward The address of the reward token
   * @return Unclaimed rewards, not including new distributions
   **/
  function getUserAccruedRewards(address user, address reward) external view returns (uint256);

  /**
   * @dev Returns a single rewards balance of a user, including virtually accrued and unrealized claimable rewards.
   * @param assets List of incentivized assets to check eligible distributions
   * @param user The address of the user
   * @param reward The address of the reward token
   * @return The rewards amount
   **/
  function getUserRewards(
    address[] calldata assets,
    address user,
    address reward
  ) external view returns (uint256);

  /**
   * @dev Returns a list all rewards of a user, including already accrued and unrealized claimable rewards
   * @param assets List of incentivized assets to check eligible distributions
   * @param user The address of the user
   * @return The list of reward addresses
   * @return The list of unclaimed amount of rewards
   **/
  function getAllUserRewards(
    address[] calldata assets,
    address user
  ) external view returns (address[] memory, uint256[] memory);

  /**
   * @dev Returns the decimals of an asset to calculate the distribution delta
   * @param asset The address to retrieve decimals
   * @return The decimals of an underlying asset
   */
  function getAssetDecimals(address asset) external view returns (uint8);

  /**
   * @dev Returns the address of the emission manager
   * @return The address of the EmissionManager
   */
  function EMISSION_MANAGER() external view returns (address);

  /**
   * @dev Returns the address of the emission manager.
   * Deprecated: This getter is maintained for compatibility purposes. Use the `EMISSION_MANAGER()` function instead.
   * @return The address of the EmissionManager
   */
  function getEmissionManager() external view returns (address);
}

File 11 of 13 : ITransferStrategyBase.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.12;

interface ITransferStrategyBase {
  event EmergencyWithdrawal(
    address indexed caller,
    address indexed token,
    address indexed to,
    uint256 amount
  );

  /**
   * @dev Perform custom transfer logic via delegate call from source contract to a TransferStrategy implementation
   * @param to Account to transfer rewards
   * @param reward Address of the reward token
   * @param amount Amount to transfer to the "to" address parameter
   * @return Returns true bool if transfer logic succeeds
   */
  function performTransfer(address to, address reward, uint256 amount) external returns (bool);

  /**
   * @return Returns the address of the Incentives Controller
   */
  function getIncentivesController() external view returns (address);

  /**
   * @return Returns the address of the Rewards admin
   */
  function getRewardsAdmin() external view returns (address);

  /**
   * @dev Perform an emergency token withdrawal only callable by the Rewards admin
   * @param token Address of the token to withdraw funds from this contract
   * @param to Address of the recipient of the withdrawal
   * @param amount Amount of the withdrawal
   */
  function emergencyWithdrawal(address token, address to, uint256 amount) external;
}

File 12 of 13 : RewardsDataTypes.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.12;

import {ITransferStrategyBase} from '../interfaces/ITransferStrategyBase.sol';
import {IEACAggregatorProxy} from '../../misc/interfaces/IEACAggregatorProxy.sol';

library RewardsDataTypes {
  struct RewardsConfigInput {
    uint88 emissionPerSecond;
    uint256 totalSupply;
    uint32 distributionEnd;
    address asset;
    address reward;
    ITransferStrategyBase transferStrategy;
    IEACAggregatorProxy rewardOracle;
  }

  struct UserAssetBalance {
    address asset;
    uint256 userBalance;
    uint256 totalSupply;
  }

  struct UserData {
    // Liquidity index of the reward distribution for the user
    uint104 index;
    // Amount of accrued rewards for the user since last user index update
    uint128 accrued;
  }

  struct RewardData {
    // Liquidity index of the reward distribution
    uint104 index;
    // Amount of reward tokens distributed per second
    uint88 emissionPerSecond;
    // Timestamp of the last reward index update
    uint32 lastUpdateTimestamp;
    // The end of the distribution of rewards (in seconds)
    uint32 distributionEnd;
    // Map of user addresses and their rewards data (userAddress => userData)
    mapping(address => UserData) usersData;
  }

  struct AssetData {
    // Map of reward token addresses and their data (rewardTokenAddress => rewardData)
    mapping(address => RewardData) rewards;
    // List of reward token addresses for the asset
    mapping(uint128 => address) availableRewards;
    // Count of reward tokens for the asset
    uint128 availableRewardsCount;
    // Number of decimals of the asset
    uint8 decimals;
  }
}

File 13 of 13 : RewardsDistributor.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {IScaledBalanceToken} from '@zerolendxyz/core-v3/contracts/interfaces/IScaledBalanceToken.sol';
import {IERC20Detailed} from '@zerolendxyz/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol';
import {SafeCast} from '@zerolendxyz/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol';
import {IRewardsDistributor} from './interfaces/IRewardsDistributor.sol';
import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
import {IVotes} from '../dependencies/openzeppelin/IVotes.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;

  uint256 public immutable maxBoostRequirement;
  IVotes public immutable staking;

  modifier onlyEmissionManager() {
    require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER');
    _;
  }

  constructor(address emissionManager, address _staking) {
    EMISSION_MANAGER = emissionManager;
    staking = IVotes(_staking);
    maxBoostRequirement = 50000000; // 50mil ZERO for max boost
  }

  /// @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 Calculates the boosted balance for an account.
   * @param account The address of the account for which to calculate the boosted balance.
   * @return The boosted balance of the account.
   **/
  function boostedBalance(address account, uint256 balance) public view returns (uint256) {
    uint256 _boosted = (balance * 20) / 100;
    uint256 _stake = staking.getVotes(account);

    uint256 _adjusted = ((balance * _stake * 80) / maxBoostRequirement) / 100;

    // because of this we are able to max out the boost by 5x
    uint256 _boostedBalance = _boosted + _adjusted;
    return _boostedBalance > balance ? balance : _boostedBalance;
  }

  /**
   * @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) {
    // recalculate user balance based on boost
    userBalance = boostedBalance(user, userBalance);

    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;
  }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_emissionManager","type":"address"},{"internalType":"address","name":"_staking","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":"account","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"}],"name":"boostedBalance","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":[],"name":"maxBoostRequirement","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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"},{"inputs":[],"name":"staking","outputs":[{"internalType":"contract IVotes","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60e060405260006005553480156200001657600080fd5b5060405162004c4338038062004c43833981016040819052620000399162000076565b6001600160a01b039182166080521660c0526302faf08060a052620000ae565b80516001600160a01b03811681146200007157600080fd5b919050565b600080604083850312156200008a57600080fd5b620000958362000059565b9150620000a56020840162000059565b90509250929050565b60805160a05160c051614b266200011d600039600081816102f50152610fa60152600081816103ff01526110190152600081816105860152818161069e01528181610d29015281816111b20152818161185901528181611a0f01528181611ab80152611bd30152614b266000f3fe608060405234801561001057600080fd5b506004361061020b5760003560e01c806385084e161161012a578063bb492bf5116100bd578063cbcbb5071161008c578063e15ac62311610071578063e15ac623146106c8578063f5cf673b146106db578063f996868b146106ee57600080fd5b8063cbcbb50714610699578063dde43cba146106c057600080fd5b8063bb492bf51461064d578063bf90f63a14610660578063c4d66de814610673578063c5a7b5381461068657600080fd5b80639efd6f72116100f95780639efd6f72146105bd5780639ff55db91461061f578063b022418c14610632578063b45ac1a91461064557600080fd5b806385084e1614610549578063886fe70b1461055c57806392074b0814610584578063955c2ad7146105aa57600080fd5b80635453ba10116101a257806369db8b271161017157806369db8b27146103fa57806370674ab91461042157806374d945ec146104345780637eff4ba81461046d57600080fd5b80635453ba101461037b57806357b898831461038e5780635f130b24146103a15780636657732f146103da57600080fd5b806333028b99116101de57806333028b99146102bc5780634c0369c3146102cf5780634cf088d9146102f0578063533f542a1461031757600080fd5b80631b839c7714610210578063236300dc146102365780632a17bf601461024957806331873e2e146102a7575b600080fd5b61022361021e366004614057565b610701565b6040519081526020015b60405180910390f35b6102236102443660046140d5565b610761565b610282610257366004614149565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603b60205260409020541690565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161022d565b6102ba6102b536600461416d565b6107fe565b005b6102236102ca3660046141a2565b61080f565b6102e26102dd366004614227565b6109bb565b60405161022d9291906142cf565b6102827f000000000000000000000000000000000000000000000000000000000000000081565b610223610325366004614326565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260016020818152604080842086861685528252808420948816845293909101905220546cffffffffffffffffffffffffff169392505050565b6102ba610389366004614057565b610d11565b61022361039c366004614366565b610dbe565b6102826103af366004614149565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603a60205260409020541690565b6103ed6103e8366004614149565b610dd8565b60405161022d91906143c5565b6102237f000000000000000000000000000000000000000000000000000000000000000081565b61022361042f3660046143d8565b610f2a565b610282610442366004614149565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603960205260409020541690565b61052961047b366004614057565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526001602090815260408083209390941682529190915220546cffffffffffffffffffffffffff8116916affffffffffffffffffffff6d01000000000000000000000000008304169163ffffffff780100000000000000000000000000000000000000000000000082048116927c01000000000000000000000000000000000000000000000000000000009092041690565b60408051948552602085019390935291830152606082015260800161022d565b610223610557366004614435565b610f41565b61056f61056a366004614057565b61108b565b6040805192835260208301919091520161022d565b7f0000000000000000000000000000000000000000000000000000000000000000610282565b6102ba6105b836600461453c565b61119a565b61060d6105cb366004614149565b73ffffffffffffffffffffffffffffffffffffffff16600090815260016020526040902060020154700100000000000000000000000000000000900460ff1690565b60405160ff909116815260200161022d565b6102e261062d3660046143d8565b61139a565b610223610640366004614057565b611549565b6103ed611602565b6102e261065b366004614227565b611671565b6102e261066e36600461466a565b61170a565b6102ba610681366004614149565b611725565b6102ba6106943660046146ac565b611841565b6102827f000000000000000000000000000000000000000000000000000000000000000081565b610223600381565b6102ba6106d6366004614057565b6119f7565b6102ba6106e9366004614057565b611aa0565b6102ba6106fc3660046146f3565b611bbb565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600160209081526040808320938516835292905220547c0100000000000000000000000000000000000000000000000000000000900463ffffffff165b92915050565b600073ffffffffffffffffffffffffffffffffffffffff83166107e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064015b60405180910390fd5b6107f48686863333888861202f565b9695505050505050565b61080a338483856122be565b505050565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152603960205260408120549091339186911682146108a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a454400000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff8616610922576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f4144445245535300000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff851661099f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107dc565b6109ae898989338a8a8a61202f565b9998505050505050505050565b60608060006109cb868686612471565b60035490915067ffffffffffffffff8111156109e9576109e9614461565b604051908082528060200260200182016040528015610a12578160200160208202803683370190505b509250825167ffffffffffffffff811115610a2f57610a2f614461565b604051908082528060200260200182016040528015610a58578160200160208202803683370190505b50915060005b8151811015610d065760005b8451811015610cf35760038181548110610a8657610a86614776565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16858281518110610ac357610ac3614776565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060016000848481518110610b1357610b13614776565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868381518110610b7057610b70614776565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16848281518110610c3657610c36614776565b60200260200101818151610c4a91906147d4565b9052508251839083908110610c6157610c61614776565b60200260200101516020015160001415610c7a57610ce1565b610cb786868381518110610c9057610c90614776565b6020026020010151858581518110610caa57610caa614776565b602002602001015161266f565b848281518110610cc957610cc9614776565b60200260200101818151610cdd91906147d4565b9052505b80610ceb816147ec565b915050610a6a565b5080610cfe816147ec565b915050610a5e565b50505b935093915050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610db0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b610dba828261273d565b5050565b6000610dcf8585853333338861202f565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600160205260408120600201546060916fffffffffffffffffffffffffffffffff909116908167ffffffffffffffff811115610e3257610e32614461565b604051908082528060200260200182016040528015610e5b578160200160208202803683370190505b50905060005b826fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161015610f225773ffffffffffffffffffffffffffffffffffffffff80861660009081526001602081815260408084206fffffffffffffffffffffffffffffffff871680865293019091529091205484519216918491908110610eeb57610eeb614776565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015280610f1a81614825565b915050610e61565b509392505050565b6000610dcf8383610f3c888888612471565b612891565b6000806064610f51846014614855565b610f5b9190614892565b6040517f9ab24eb000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301529192506000917f00000000000000000000000000000000000000000000000000000000000000001690639ab24eb090602401602060405180830381865afa158015610fed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101191906148cd565b9050600060647f00000000000000000000000000000000000000000000000000000000000000006110428488614855565b61104d906050614855565b6110579190614892565b6110619190614892565b9050600061106f82856147d4565b905085811161107e5780611080565b855b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff8083166000818152600160209081526040808320948616835293815283822084517fb1bf962d000000000000000000000000000000000000000000000000000000008152945192948594919361118d9385939263b1bf962d92600480830193928290030181865afa158015611119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113d91906148cd565b73ffffffffffffffffffffffffffffffffffffffff881660009081526001602052604090206002015461118890700100000000000000000000000000000000900460ff16600a614a06565b612a30565b92509250505b9250929050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611239576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b60005b815181101561138d5781818151811061125757611257614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d191906148cd565b8282815181106112e3576112e3614776565b6020026020010151602001818152505061133782828151811061130857611308614776565b60200260200101516080015183838151811061132657611326614776565b602002602001015160a00151612b3c565b61137b82828151811061134c5761134c614776565b60200260200101516080015183838151811061136a5761136a614776565b602002602001015160c0015161273d565b80611385816147ec565b91505061123c565b5061139781612ca2565b50565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260396020526040902054606091829133918691168214611433576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a454400000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff86166114b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f4144445245535300000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff851661152d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107dc565b61153a8888338989613543565b93509350505094509492505050565b60008060005b600454811015610f2257600160006004838154811061157057611570614776565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff9081168452838201949094526040928301822088851683528152828220938916825260019093019092529020546115ee906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16836147d4565b9150806115fa816147ec565b91505061154f565b6060600380548060200260200160405190810160405280929190818152602001828054801561166757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161163c575b5050505050905090565b60608073ffffffffffffffffffffffffffffffffffffffff83166116f1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107dc565b6116fe8585333387613543565b91509150935093915050565b60608061171a8484333333613543565b915091509250929050565b60065460039060ff16806117385750303b155b80611744575060055481115b6117d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560448201527f656e20696e697469616c697a656400000000000000000000000000000000000060648201526084016107dc565b60065460ff1615801561180e57600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560058290555b801561080a57600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146118e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167c010000000000000000000000000000000000000000000000000000000063ffffffff8981168281029384179586905587516d01000000000000000000000000009096046affffffffffffffffffffff16808752968601969096529083041694830185905260608301939093526cffffffffffffffffffffffffff9081169216919091176080820152909291907fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59060a00160405180910390a350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611a96576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b610dba8282612b3c565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611b3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff82811660008181526039602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611c5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b828114611cc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f494e56414c49445f494e5055540000000000000000000000000000000000000060448201526064016107dc565b60005b838110156120275773ffffffffffffffffffffffffffffffffffffffff86166000908152600160205260408120908181888886818110611d0857611d08614776565b9050602002016020810190611d1d9190614149565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002830154909150700100000000000000000000000000000000900460ff168015801590611d93575081547801000000000000000000000000000000000000000000000000900463ffffffff1615155b611df9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f444953545249425554494f4e5f444f45535f4e4f545f4558495354000000000060448201526064016107dc565b6000611e7e838b73ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6e91906148cd565b611e7985600a614a15565b613a2b565b5083549091506d010000000000000000000000000090046affffffffffffffffffffff16878787818110611eb457611eb4614776565b9050602002016020810190611ec99190614a21565b84546affffffffffffffffffffff919091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff909116178455898987818110611f2457611f24614776565b9050602002016020810190611f399190614149565b73ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5838b8b8b818110611f9a57611f9a614776565b9050602002016020810190611faf9190614a21565b8854604080519384526affffffffffffffffffffff90921660208401527c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690820181905260608201526080810186905260a00160405180910390a35050505050808061201f906147ec565b915050611cc6565b505050505050565b60008561203e57506000611080565b60006120548561204f8b8b89612471565b613bb9565b60005b8881101561223b5760008a8a8381811061207357612073614776565b90506020020160208101906120889190614149565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526001602081815260408084208b861685528252808420948d16845293909101905220549091506120f8906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16846147d4565b925088831161216b5773ffffffffffffffffffffffffffffffffffffffff80821660009081526001602081815260408084208a861685528252808420948c1684529390910190522080547fffffff00000000000000000000000000000000ffffffffffffffffffffffffff169055612228565b60006121778a85614a3c565b90506121838185614a3c565b935061218e81613c3a565b73ffffffffffffffffffffffffffffffffffffffff92831660009081526001602081815260408084208b881685528252808420968d1684529590910190529290922080546fffffffffffffffffffffffffffffffff939093166d0100000000000000000000000000027fffffff00000000000000000000000000000000ffffffffffffffffffffffffff909316929092179091555061223b565b5080612233816147ec565b915050612057565b508061224b576000915050611080565b612256848483613ce0565b6040805173ffffffffffffffffffffffffffffffffffffffff8881168252602082018490528087169286821692918916917fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f004910160405180910390a498975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff841660009081526001602052604090206002015460ff700100000000000000000000000000000000820416600a0a906fffffffffffffffffffffffffffffffff168061232057505061246b565b60005b81816fffffffffffffffffffffffffffffffff1610156124675773ffffffffffffffffffffffffffffffffffffffff80881660009081526001602081815260408084206fffffffffffffffffffffffffffffffff87168552928301825280842054909416808452919052918120908061239d838989613a2b565b915091506000806123b1858d8d878d613e0c565b9150915082806123be5750805b15612455578b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167f3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe887888760405161244c939291909283526020830191909152604082015260600190565b60405180910390a45b50506001909401935061232392505050565b5050505b50505050565b60608267ffffffffffffffff81111561248c5761248c614461565b6040519080825280602002602001820160405280156124f757816020015b6124e46040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b8152602001906001900390816124aa5790505b50905060005b83811015610f225784848281811061251757612517614776565b905060200201602081019061252c9190614149565b82828151811061253e5761253e614776565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff909116905284848281811061257557612575614776565b905060200201602081019061258a9190614149565b6040517f0afbcdc900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301529190911690630afbcdc9906024016040805180830381865afa1580156125f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061261b9190614a53565b83838151811061262d5761262d614776565b602002602001015160200184848151811061264a5761264a614776565b6020908102919091010151604001919091525280612667816147ec565b9150506124fd565b805173ffffffffffffffffffffffffffffffffffffffff9081166000908152600160208181526040808420878616855282528084208651909516845291905281206002015490919082906126db90700100000000000000000000000000000000900460ff16600a614a06565b905060006126ee83866040015184612a30565b60208088015173ffffffffffffffffffffffffffffffffffffffff8b1660009081526001880190925260409091205491935061108092509083906cffffffffffffffffffffffffff1685613f7b565b60008173ffffffffffffffffffffffffffffffffffffffff166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561278a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127ae91906148cd565b13612815576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f5241434c455f4d5553545f52455455524e5f5052494345000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603b602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f1a1cd5483e52e60b9ff7f3b9d1db3bbd9e9d21c6324ad3a8c79dba9b75e62f4d9190a35050565b6000805b8251811015610f22578281815181106128b0576128b0614776565b6020026020010151602001516000141561295f57600160008483815181106128da576128da614776565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff9081168352828201939093526040918201600090812088851682528252828120938916815260019093019052902054612958906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16836147d4565b9150612a1e565b6001600084838151811061297557612975614776565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205483516d01000000000000000000000000009091046fffffffffffffffffffffffffffffffff1690612a079087908790879086908110610caa57610caa614776565b612a1191906147d4565b612a1b90836147d4565b91505b80612a28816147ec565b915050612895565b825460009081906cffffffffffffffffffffffffff81169063ffffffff7c010000000000000000000000000000000000000000000000000000000082048116916affffffffffffffffffffff6d010000000000000000000000000082041691780100000000000000000000000000000000000000000000000090910416811580612ab8575087155b80612ac257504281145b80612acd5750828110155b15612ae15783849550955050505050610d09565b6000834211612af05742612af2565b835b90506000612b008383614a3c565b9050600089612b0f8387614855565b612b199190614855565b8b9004905086612b2981836147d4565b9850985050505050505050935093915050565b73ffffffffffffffffffffffffffffffffffffffff8116612bb9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f53545241544547595f43414e5f4e4f545f42455f5a45524f000000000000000060448201526064016107dc565b6001813b151514612c26576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53545241544547595f4d5553545f42455f434f4e54524143540000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f8ca1d928f1d72493a6b78c4f74aabde976bc37ffe2570f2a1ce5a8abd3dde0aa9190a35050565b60005b8151811015610dba5760016000838381518110612cc457612cc4614776565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002060020154700100000000000000000000000000000000900460ff16612d90576004828281518110612d2b57612d2b614776565b6020908102919091018101516060015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b6000828281518110612da457612da4614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1e9190614a77565b60016000858581518110612e3457612e34614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160106101000a81548160ff021916908360ff160217905560ff169050600060016000858581518110612eb157612eb1614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000858581518110612f0e57612f0e614776565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002080549091507801000000000000000000000000000000000000000000000000900463ffffffff1661317d57838381518110612f7f57612f7f614776565b60200260200101516080015160016000868681518110612fa157612fa1614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160006001600088888151811061300257613002614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160008585815181106130ee576130ee614776565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600090812060020180546fffffffffffffffffffffffffffffffff169161314583614825565b91906101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550505b6002600085858151811061319357613193614776565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff166132bd576001600260008686815181106131e7576131e7614776565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600384848151811061325857613258614776565b6020908102919091018101516080015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b60006132ee828686815181106132d5576132d5614776565b60200260200101516020015185600a611e799190614a15565b50825486519192506d010000000000000000000000000081046affffffffffffffffffffff16917c010000000000000000000000000000000000000000000000000000000090910463ffffffff169087908790811061334f5761334f614776565b60209081029190910101515184546affffffffffffffffffffff9091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff90911617845586518790879081106133b7576133b7614776565b602090810291909101015160400151845463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116178455865187908790811061342657613426614776565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1687878151811061345a5761345a614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5848a8a815181106134b0576134b0614776565b602002602001015160000151858c8c815181106134cf576134cf614776565b602002602001015160400151896040516135239594939291906affffffffffffffffffffff958616815293909416602084015263ffffffff9182166040840152166060820152608081019190915260a00190565b60405180910390a35050505050808061353b906147ec565b915050612ca5565b60035460609081908067ffffffffffffffff81111561356457613564614461565b60405190808252806020026020018201604052801561358d578160200160208202803683370190505b5092508067ffffffffffffffff8111156135a9576135a9614461565b6040519080825280602002602001820160405280156135d2578160200160208202803683370190505b5091506135e48561204f8a8a89612471565b60005b878110156138f357600089898381811061360357613603614776565b90506020020160208101906136189190614149565b905060005b838110156138de57600073ffffffffffffffffffffffffffffffffffffffff1686828151811061364f5761364f614776565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156136fe576003818154811061368657613686614776565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168682815181106136c3576136c3614776565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604081208751829089908590811061373957613739614776565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683528282019390935260409182016000908120938d168152600190930190529020546d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16905080156138cb57808683815181106137bd576137bd614776565b602002602001018181516137d191906147d4565b90525073ffffffffffffffffffffffffffffffffffffffff83166000908152600160205260408120885182908a908690811061380f5761380f614776565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b50806138d6816147ec565b91505061361d565b505080806138eb906147ec565b9150506135e7565b5060005b81811015613a1f5761393c8585838151811061391557613915614776565b602002602001015185848151811061392f5761392f614776565b6020026020010151613ce0565b8473ffffffffffffffffffffffffffffffffffffffff1684828151811061396557613965614776565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a8786815181106139ce576139ce614776565b6020026020010151604051613a0592919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b60405180910390a480613a17816147ec565b9150506138f7565b50509550959350505050565b600080600080613a3c878787612a30565b915091506000828214613b55576cffffffffffffffffffffffffff821115613ac0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e4445585f4f564552464c4f5700000000000000000000000000000000000060448201526064016107dc565b5086547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff82161787556001613b0342613f9f565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff909116178855613bac565b613b5e42613f9f565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788555b9097909650945050505050565b60005b815181101561080a57613c28828281518110613bda57613bda614776565b60200260200101516000015184848481518110613bf957613bf9614776565b602002602001015160200151858581518110613c1757613c17614776565b6020026020010151604001516122be565b80613c32816147ec565b915050613bbc565b60006fffffffffffffffffffffffffffffffff821115613cdc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f323820626974730000000000000000000000000000000000000000000000000060648201526084016107dc565b5090565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a60205260408082205490517f16beb9820000000000000000000000000000000000000000000000000000000081528785166004820152602481019390935260448301859052909216919082906316beb982906064016020604051808303816000875af1158015613d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d979190614a9a565b9050600181151514613e05576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5452414e534645525f4552524f5200000000000000000000000000000000000060448201526064016107dc565b5050505050565b600080613e198686610f41565b73ffffffffffffffffffffffffffffffffffffffff871660009081526001890160205260408120549196506cffffffffffffffffffffffffff90911690858214801590613f6c5773ffffffffffffffffffffffffffffffffffffffff8916600090815260018b016020526040902080547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff89161790558715613f6c57613ed088888589613f7b565b9150613edb82613c3a565b73ffffffffffffffffffffffffffffffffffffffff8a16600090815260018c01602052604090208054600d90613f359084906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16614abc565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b90999098509650505050505050565b600080613f888486614a3c565b613f929087614855565b9290920495945050505050565b600063ffffffff821115613cdc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f322062697473000000000000000000000000000000000000000000000000000060648201526084016107dc565b73ffffffffffffffffffffffffffffffffffffffff8116811461139757600080fd5b6000806040838503121561406a57600080fd5b823561407581614035565b9150602083013561408581614035565b809150509250929050565b60008083601f8401126140a257600080fd5b50813567ffffffffffffffff8111156140ba57600080fd5b6020830191508360208260051b850101111561119357600080fd5b6000806000806000608086880312156140ed57600080fd5b853567ffffffffffffffff81111561410457600080fd5b61411088828901614090565b90965094505060208601359250604086013561412b81614035565b9150606086013561413b81614035565b809150509295509295909350565b60006020828403121561415b57600080fd5b813561416681614035565b9392505050565b60008060006060848603121561418257600080fd5b833561418d81614035565b95602085013595506040909401359392505050565b60008060008060008060a087890312156141bb57600080fd5b863567ffffffffffffffff8111156141d257600080fd5b6141de89828a01614090565b9097509550506020870135935060408701356141f981614035565b9250606087013561420981614035565b9150608087013561421981614035565b809150509295509295509295565b60008060006040848603121561423c57600080fd5b833567ffffffffffffffff81111561425357600080fd5b61425f86828701614090565b909450925050602084013561427381614035565b809150509250925092565b600081518084526020808501945080840160005b838110156142c457815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614292565b509495945050505050565b6040815260006142e2604083018561427e565b82810360208481019190915284518083528582019282019060005b81811015614319578451835293830193918301916001016142fd565b5090979650505050505050565b60008060006060848603121561433b57600080fd5b833561434681614035565b9250602084013561435681614035565b9150604084013561427381614035565b6000806000806060858703121561437c57600080fd5b843567ffffffffffffffff81111561439357600080fd5b61439f87828801614090565b9095509350506020850135915060408501356143ba81614035565b939692955090935050565b602081526000614166602083018461427e565b600080600080606085870312156143ee57600080fd5b843567ffffffffffffffff81111561440557600080fd5b61441187828801614090565b909550935050602085013561442581614035565b915060408501356143ba81614035565b6000806040838503121561444857600080fd5b823561445381614035565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff811182821017156144b3576144b3614461565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561450057614500614461565b604052919050565b80356affffffffffffffffffffff8116811461452357600080fd5b919050565b803563ffffffff8116811461452357600080fd5b6000602080838503121561454f57600080fd5b823567ffffffffffffffff8082111561456757600080fd5b818501915085601f83011261457b57600080fd5b81358181111561458d5761458d614461565b61459b848260051b016144b9565b818152848101925060e09182028401850191888311156145ba57600080fd5b938501935b8285101561465e5780858a0312156145d75760008081fd5b6145df614490565b6145e886614508565b8152868601358782015260406145ff818801614528565b9082015260608681013561461281614035565b9082015260808681013561462581614035565b9082015260a08681013561463881614035565b9082015260c08681013561464b81614035565b90820152845293840193928501926145bf565b50979650505050505050565b6000806020838503121561467d57600080fd5b823567ffffffffffffffff81111561469457600080fd5b6146a085828601614090565b90969095509350505050565b6000806000606084860312156146c157600080fd5b83356146cc81614035565b925060208401356146dc81614035565b91506146ea60408501614528565b90509250925092565b60008060008060006060868803121561470b57600080fd5b853561471681614035565b9450602086013567ffffffffffffffff8082111561473357600080fd5b61473f89838a01614090565b9096509450604088013591508082111561475857600080fd5b5061476588828901614090565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156147e7576147e76147a5565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561481e5761481e6147a5565b5060010190565b60006fffffffffffffffffffffffffffffffff8083168181141561484b5761484b6147a5565b6001019392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561488d5761488d6147a5565b500290565b6000826148c8577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000602082840312156148df57600080fd5b5051919050565b600181815b8085111561493f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614925576149256147a5565b8085161561493257918102915b93841c93908002906148eb565b509250929050565b6000826149565750600161075b565b816149635750600061075b565b816001811461497957600281146149835761499f565b600191505061075b565b60ff841115614994576149946147a5565b50506001821b61075b565b5060208310610133831016604e8410600b84101617156149c2575081810a61075b565b6149cc83836148e6565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156149fe576149fe6147a5565b029392505050565b600061416660ff841683614947565b60006141668383614947565b600060208284031215614a3357600080fd5b61416682614508565b600082821015614a4e57614a4e6147a5565b500390565b60008060408385031215614a6657600080fd5b505080516020909101519092909150565b600060208284031215614a8957600080fd5b815160ff8116811461416657600080fd5b600060208284031215614aac57600080fd5b8151801515811461416657600080fd5b60006fffffffffffffffffffffffffffffffff808316818516808303821115614ae757614ae76147a5565b0194935050505056fea26469706673582212208b72c2a3f7280c8e5f94d3a7301662a8a62092f21ab26dafc55e219f7376944264736f6c634300080c0033000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba50000000000000000000000002666951a62d82860e8e1385581e2fb7669097647

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061020b5760003560e01c806385084e161161012a578063bb492bf5116100bd578063cbcbb5071161008c578063e15ac62311610071578063e15ac623146106c8578063f5cf673b146106db578063f996868b146106ee57600080fd5b8063cbcbb50714610699578063dde43cba146106c057600080fd5b8063bb492bf51461064d578063bf90f63a14610660578063c4d66de814610673578063c5a7b5381461068657600080fd5b80639efd6f72116100f95780639efd6f72146105bd5780639ff55db91461061f578063b022418c14610632578063b45ac1a91461064557600080fd5b806385084e1614610549578063886fe70b1461055c57806392074b0814610584578063955c2ad7146105aa57600080fd5b80635453ba10116101a257806369db8b271161017157806369db8b27146103fa57806370674ab91461042157806374d945ec146104345780637eff4ba81461046d57600080fd5b80635453ba101461037b57806357b898831461038e5780635f130b24146103a15780636657732f146103da57600080fd5b806333028b99116101de57806333028b99146102bc5780634c0369c3146102cf5780634cf088d9146102f0578063533f542a1461031757600080fd5b80631b839c7714610210578063236300dc146102365780632a17bf601461024957806331873e2e146102a7575b600080fd5b61022361021e366004614057565b610701565b6040519081526020015b60405180910390f35b6102236102443660046140d5565b610761565b610282610257366004614149565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603b60205260409020541690565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161022d565b6102ba6102b536600461416d565b6107fe565b005b6102236102ca3660046141a2565b61080f565b6102e26102dd366004614227565b6109bb565b60405161022d9291906142cf565b6102827f0000000000000000000000002666951a62d82860e8e1385581e2fb766909764781565b610223610325366004614326565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260016020818152604080842086861685528252808420948816845293909101905220546cffffffffffffffffffffffffff169392505050565b6102ba610389366004614057565b610d11565b61022361039c366004614366565b610dbe565b6102826103af366004614149565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603a60205260409020541690565b6103ed6103e8366004614149565b610dd8565b60405161022d91906143c5565b6102237f0000000000000000000000000000000000000000000000000000000002faf08081565b61022361042f3660046143d8565b610f2a565b610282610442366004614149565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603960205260409020541690565b61052961047b366004614057565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526001602090815260408083209390941682529190915220546cffffffffffffffffffffffffff8116916affffffffffffffffffffff6d01000000000000000000000000008304169163ffffffff780100000000000000000000000000000000000000000000000082048116927c01000000000000000000000000000000000000000000000000000000009092041690565b60408051948552602085019390935291830152606082015260800161022d565b610223610557366004614435565b610f41565b61056f61056a366004614057565b61108b565b6040805192835260208301919091520161022d565b7f000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba5610282565b6102ba6105b836600461453c565b61119a565b61060d6105cb366004614149565b73ffffffffffffffffffffffffffffffffffffffff16600090815260016020526040902060020154700100000000000000000000000000000000900460ff1690565b60405160ff909116815260200161022d565b6102e261062d3660046143d8565b61139a565b610223610640366004614057565b611549565b6103ed611602565b6102e261065b366004614227565b611671565b6102e261066e36600461466a565b61170a565b6102ba610681366004614149565b611725565b6102ba6106943660046146ac565b611841565b6102827f000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba581565b610223600381565b6102ba6106d6366004614057565b6119f7565b6102ba6106e9366004614057565b611aa0565b6102ba6106fc3660046146f3565b611bbb565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600160209081526040808320938516835292905220547c0100000000000000000000000000000000000000000000000000000000900463ffffffff165b92915050565b600073ffffffffffffffffffffffffffffffffffffffff83166107e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064015b60405180910390fd5b6107f48686863333888861202f565b9695505050505050565b61080a338483856122be565b505050565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152603960205260408120549091339186911682146108a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a454400000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff8616610922576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f4144445245535300000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff851661099f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107dc565b6109ae898989338a8a8a61202f565b9998505050505050505050565b60608060006109cb868686612471565b60035490915067ffffffffffffffff8111156109e9576109e9614461565b604051908082528060200260200182016040528015610a12578160200160208202803683370190505b509250825167ffffffffffffffff811115610a2f57610a2f614461565b604051908082528060200260200182016040528015610a58578160200160208202803683370190505b50915060005b8151811015610d065760005b8451811015610cf35760038181548110610a8657610a86614776565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16858281518110610ac357610ac3614776565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060016000848481518110610b1357610b13614776565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868381518110610b7057610b70614776565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16848281518110610c3657610c36614776565b60200260200101818151610c4a91906147d4565b9052508251839083908110610c6157610c61614776565b60200260200101516020015160001415610c7a57610ce1565b610cb786868381518110610c9057610c90614776565b6020026020010151858581518110610caa57610caa614776565b602002602001015161266f565b848281518110610cc957610cc9614776565b60200260200101818151610cdd91906147d4565b9052505b80610ceb816147ec565b915050610a6a565b5080610cfe816147ec565b915050610a5e565b50505b935093915050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba51614610db0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b610dba828261273d565b5050565b6000610dcf8585853333338861202f565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600160205260408120600201546060916fffffffffffffffffffffffffffffffff909116908167ffffffffffffffff811115610e3257610e32614461565b604051908082528060200260200182016040528015610e5b578160200160208202803683370190505b50905060005b826fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161015610f225773ffffffffffffffffffffffffffffffffffffffff80861660009081526001602081815260408084206fffffffffffffffffffffffffffffffff871680865293019091529091205484519216918491908110610eeb57610eeb614776565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015280610f1a81614825565b915050610e61565b509392505050565b6000610dcf8383610f3c888888612471565b612891565b6000806064610f51846014614855565b610f5b9190614892565b6040517f9ab24eb000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301529192506000917f0000000000000000000000002666951a62d82860e8e1385581e2fb76690976471690639ab24eb090602401602060405180830381865afa158015610fed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101191906148cd565b9050600060647f0000000000000000000000000000000000000000000000000000000002faf0806110428488614855565b61104d906050614855565b6110579190614892565b6110619190614892565b9050600061106f82856147d4565b905085811161107e5780611080565b855b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff8083166000818152600160209081526040808320948616835293815283822084517fb1bf962d000000000000000000000000000000000000000000000000000000008152945192948594919361118d9385939263b1bf962d92600480830193928290030181865afa158015611119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113d91906148cd565b73ffffffffffffffffffffffffffffffffffffffff881660009081526001602052604090206002015461118890700100000000000000000000000000000000900460ff16600a614a06565b612a30565b92509250505b9250929050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba51614611239576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b60005b815181101561138d5781818151811061125757611257614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d191906148cd565b8282815181106112e3576112e3614776565b6020026020010151602001818152505061133782828151811061130857611308614776565b60200260200101516080015183838151811061132657611326614776565b602002602001015160a00151612b3c565b61137b82828151811061134c5761134c614776565b60200260200101516080015183838151811061136a5761136a614776565b602002602001015160c0015161273d565b80611385816147ec565b91505061123c565b5061139781612ca2565b50565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260396020526040902054606091829133918691168214611433576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a454400000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff86166114b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f4144445245535300000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff851661152d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107dc565b61153a8888338989613543565b93509350505094509492505050565b60008060005b600454811015610f2257600160006004838154811061157057611570614776565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff9081168452838201949094526040928301822088851683528152828220938916825260019093019092529020546115ee906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16836147d4565b9150806115fa816147ec565b91505061154f565b6060600380548060200260200160405190810160405280929190818152602001828054801561166757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161163c575b5050505050905090565b60608073ffffffffffffffffffffffffffffffffffffffff83166116f1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107dc565b6116fe8585333387613543565b91509150935093915050565b60608061171a8484333333613543565b915091509250929050565b60065460039060ff16806117385750303b155b80611744575060055481115b6117d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560448201527f656e20696e697469616c697a656400000000000000000000000000000000000060648201526084016107dc565b60065460ff1615801561180e57600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560058290555b801561080a57600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba516146118e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167c010000000000000000000000000000000000000000000000000000000063ffffffff8981168281029384179586905587516d01000000000000000000000000009096046affffffffffffffffffffff16808752968601969096529083041694830185905260608301939093526cffffffffffffffffffffffffff9081169216919091176080820152909291907fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59060a00160405180910390a350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba51614611a96576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b610dba8282612b3c565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba51614611b3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff82811660008181526039602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba51614611c5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107dc565b828114611cc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f494e56414c49445f494e5055540000000000000000000000000000000000000060448201526064016107dc565b60005b838110156120275773ffffffffffffffffffffffffffffffffffffffff86166000908152600160205260408120908181888886818110611d0857611d08614776565b9050602002016020810190611d1d9190614149565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002830154909150700100000000000000000000000000000000900460ff168015801590611d93575081547801000000000000000000000000000000000000000000000000900463ffffffff1615155b611df9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f444953545249425554494f4e5f444f45535f4e4f545f4558495354000000000060448201526064016107dc565b6000611e7e838b73ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6e91906148cd565b611e7985600a614a15565b613a2b565b5083549091506d010000000000000000000000000090046affffffffffffffffffffff16878787818110611eb457611eb4614776565b9050602002016020810190611ec99190614a21565b84546affffffffffffffffffffff919091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff909116178455898987818110611f2457611f24614776565b9050602002016020810190611f399190614149565b73ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5838b8b8b818110611f9a57611f9a614776565b9050602002016020810190611faf9190614a21565b8854604080519384526affffffffffffffffffffff90921660208401527c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690820181905260608201526080810186905260a00160405180910390a35050505050808061201f906147ec565b915050611cc6565b505050505050565b60008561203e57506000611080565b60006120548561204f8b8b89612471565b613bb9565b60005b8881101561223b5760008a8a8381811061207357612073614776565b90506020020160208101906120889190614149565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526001602081815260408084208b861685528252808420948d16845293909101905220549091506120f8906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16846147d4565b925088831161216b5773ffffffffffffffffffffffffffffffffffffffff80821660009081526001602081815260408084208a861685528252808420948c1684529390910190522080547fffffff00000000000000000000000000000000ffffffffffffffffffffffffff169055612228565b60006121778a85614a3c565b90506121838185614a3c565b935061218e81613c3a565b73ffffffffffffffffffffffffffffffffffffffff92831660009081526001602081815260408084208b881685528252808420968d1684529590910190529290922080546fffffffffffffffffffffffffffffffff939093166d0100000000000000000000000000027fffffff00000000000000000000000000000000ffffffffffffffffffffffffff909316929092179091555061223b565b5080612233816147ec565b915050612057565b508061224b576000915050611080565b612256848483613ce0565b6040805173ffffffffffffffffffffffffffffffffffffffff8881168252602082018490528087169286821692918916917fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f004910160405180910390a498975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff841660009081526001602052604090206002015460ff700100000000000000000000000000000000820416600a0a906fffffffffffffffffffffffffffffffff168061232057505061246b565b60005b81816fffffffffffffffffffffffffffffffff1610156124675773ffffffffffffffffffffffffffffffffffffffff80881660009081526001602081815260408084206fffffffffffffffffffffffffffffffff87168552928301825280842054909416808452919052918120908061239d838989613a2b565b915091506000806123b1858d8d878d613e0c565b9150915082806123be5750805b15612455578b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167f3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe887888760405161244c939291909283526020830191909152604082015260600190565b60405180910390a45b50506001909401935061232392505050565b5050505b50505050565b60608267ffffffffffffffff81111561248c5761248c614461565b6040519080825280602002602001820160405280156124f757816020015b6124e46040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b8152602001906001900390816124aa5790505b50905060005b83811015610f225784848281811061251757612517614776565b905060200201602081019061252c9190614149565b82828151811061253e5761253e614776565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff909116905284848281811061257557612575614776565b905060200201602081019061258a9190614149565b6040517f0afbcdc900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301529190911690630afbcdc9906024016040805180830381865afa1580156125f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061261b9190614a53565b83838151811061262d5761262d614776565b602002602001015160200184848151811061264a5761264a614776565b6020908102919091010151604001919091525280612667816147ec565b9150506124fd565b805173ffffffffffffffffffffffffffffffffffffffff9081166000908152600160208181526040808420878616855282528084208651909516845291905281206002015490919082906126db90700100000000000000000000000000000000900460ff16600a614a06565b905060006126ee83866040015184612a30565b60208088015173ffffffffffffffffffffffffffffffffffffffff8b1660009081526001880190925260409091205491935061108092509083906cffffffffffffffffffffffffff1685613f7b565b60008173ffffffffffffffffffffffffffffffffffffffff166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561278a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127ae91906148cd565b13612815576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f5241434c455f4d5553545f52455455524e5f5052494345000000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603b602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f1a1cd5483e52e60b9ff7f3b9d1db3bbd9e9d21c6324ad3a8c79dba9b75e62f4d9190a35050565b6000805b8251811015610f22578281815181106128b0576128b0614776565b6020026020010151602001516000141561295f57600160008483815181106128da576128da614776565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff9081168352828201939093526040918201600090812088851682528252828120938916815260019093019052902054612958906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16836147d4565b9150612a1e565b6001600084838151811061297557612975614776565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205483516d01000000000000000000000000009091046fffffffffffffffffffffffffffffffff1690612a079087908790879086908110610caa57610caa614776565b612a1191906147d4565b612a1b90836147d4565b91505b80612a28816147ec565b915050612895565b825460009081906cffffffffffffffffffffffffff81169063ffffffff7c010000000000000000000000000000000000000000000000000000000082048116916affffffffffffffffffffff6d010000000000000000000000000082041691780100000000000000000000000000000000000000000000000090910416811580612ab8575087155b80612ac257504281145b80612acd5750828110155b15612ae15783849550955050505050610d09565b6000834211612af05742612af2565b835b90506000612b008383614a3c565b9050600089612b0f8387614855565b612b199190614855565b8b9004905086612b2981836147d4565b9850985050505050505050935093915050565b73ffffffffffffffffffffffffffffffffffffffff8116612bb9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f53545241544547595f43414e5f4e4f545f42455f5a45524f000000000000000060448201526064016107dc565b6001813b151514612c26576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53545241544547595f4d5553545f42455f434f4e54524143540000000000000060448201526064016107dc565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f8ca1d928f1d72493a6b78c4f74aabde976bc37ffe2570f2a1ce5a8abd3dde0aa9190a35050565b60005b8151811015610dba5760016000838381518110612cc457612cc4614776565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002060020154700100000000000000000000000000000000900460ff16612d90576004828281518110612d2b57612d2b614776565b6020908102919091018101516060015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b6000828281518110612da457612da4614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612dfa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1e9190614a77565b60016000858581518110612e3457612e34614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160106101000a81548160ff021916908360ff160217905560ff169050600060016000858581518110612eb157612eb1614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000858581518110612f0e57612f0e614776565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002080549091507801000000000000000000000000000000000000000000000000900463ffffffff1661317d57838381518110612f7f57612f7f614776565b60200260200101516080015160016000868681518110612fa157612fa1614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160006001600088888151811061300257613002614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160008585815181106130ee576130ee614776565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600090812060020180546fffffffffffffffffffffffffffffffff169161314583614825565b91906101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550505b6002600085858151811061319357613193614776565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff166132bd576001600260008686815181106131e7576131e7614776565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600384848151811061325857613258614776565b6020908102919091018101516080015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b60006132ee828686815181106132d5576132d5614776565b60200260200101516020015185600a611e799190614a15565b50825486519192506d010000000000000000000000000081046affffffffffffffffffffff16917c010000000000000000000000000000000000000000000000000000000090910463ffffffff169087908790811061334f5761334f614776565b60209081029190910101515184546affffffffffffffffffffff9091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff90911617845586518790879081106133b7576133b7614776565b602090810291909101015160400151845463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116178455865187908790811061342657613426614776565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1687878151811061345a5761345a614776565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5848a8a815181106134b0576134b0614776565b602002602001015160000151858c8c815181106134cf576134cf614776565b602002602001015160400151896040516135239594939291906affffffffffffffffffffff958616815293909416602084015263ffffffff9182166040840152166060820152608081019190915260a00190565b60405180910390a35050505050808061353b906147ec565b915050612ca5565b60035460609081908067ffffffffffffffff81111561356457613564614461565b60405190808252806020026020018201604052801561358d578160200160208202803683370190505b5092508067ffffffffffffffff8111156135a9576135a9614461565b6040519080825280602002602001820160405280156135d2578160200160208202803683370190505b5091506135e48561204f8a8a89612471565b60005b878110156138f357600089898381811061360357613603614776565b90506020020160208101906136189190614149565b905060005b838110156138de57600073ffffffffffffffffffffffffffffffffffffffff1686828151811061364f5761364f614776565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156136fe576003818154811061368657613686614776565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168682815181106136c3576136c3614776565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604081208751829089908590811061373957613739614776565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683528282019390935260409182016000908120938d168152600190930190529020546d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16905080156138cb57808683815181106137bd576137bd614776565b602002602001018181516137d191906147d4565b90525073ffffffffffffffffffffffffffffffffffffffff83166000908152600160205260408120885182908a908690811061380f5761380f614776565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b50806138d6816147ec565b91505061361d565b505080806138eb906147ec565b9150506135e7565b5060005b81811015613a1f5761393c8585838151811061391557613915614776565b602002602001015185848151811061392f5761392f614776565b6020026020010151613ce0565b8473ffffffffffffffffffffffffffffffffffffffff1684828151811061396557613965614776565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a8786815181106139ce576139ce614776565b6020026020010151604051613a0592919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b60405180910390a480613a17816147ec565b9150506138f7565b50509550959350505050565b600080600080613a3c878787612a30565b915091506000828214613b55576cffffffffffffffffffffffffff821115613ac0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e4445585f4f564552464c4f5700000000000000000000000000000000000060448201526064016107dc565b5086547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff82161787556001613b0342613f9f565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff909116178855613bac565b613b5e42613f9f565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788555b9097909650945050505050565b60005b815181101561080a57613c28828281518110613bda57613bda614776565b60200260200101516000015184848481518110613bf957613bf9614776565b602002602001015160200151858581518110613c1757613c17614776565b6020026020010151604001516122be565b80613c32816147ec565b915050613bbc565b60006fffffffffffffffffffffffffffffffff821115613cdc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f323820626974730000000000000000000000000000000000000000000000000060648201526084016107dc565b5090565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a60205260408082205490517f16beb9820000000000000000000000000000000000000000000000000000000081528785166004820152602481019390935260448301859052909216919082906316beb982906064016020604051808303816000875af1158015613d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d979190614a9a565b9050600181151514613e05576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5452414e534645525f4552524f5200000000000000000000000000000000000060448201526064016107dc565b5050505050565b600080613e198686610f41565b73ffffffffffffffffffffffffffffffffffffffff871660009081526001890160205260408120549196506cffffffffffffffffffffffffff90911690858214801590613f6c5773ffffffffffffffffffffffffffffffffffffffff8916600090815260018b016020526040902080547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff89161790558715613f6c57613ed088888589613f7b565b9150613edb82613c3a565b73ffffffffffffffffffffffffffffffffffffffff8a16600090815260018c01602052604090208054600d90613f359084906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16614abc565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b90999098509650505050505050565b600080613f888486614a3c565b613f929087614855565b9290920495945050505050565b600063ffffffff821115613cdc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f322062697473000000000000000000000000000000000000000000000000000060648201526084016107dc565b73ffffffffffffffffffffffffffffffffffffffff8116811461139757600080fd5b6000806040838503121561406a57600080fd5b823561407581614035565b9150602083013561408581614035565b809150509250929050565b60008083601f8401126140a257600080fd5b50813567ffffffffffffffff8111156140ba57600080fd5b6020830191508360208260051b850101111561119357600080fd5b6000806000806000608086880312156140ed57600080fd5b853567ffffffffffffffff81111561410457600080fd5b61411088828901614090565b90965094505060208601359250604086013561412b81614035565b9150606086013561413b81614035565b809150509295509295909350565b60006020828403121561415b57600080fd5b813561416681614035565b9392505050565b60008060006060848603121561418257600080fd5b833561418d81614035565b95602085013595506040909401359392505050565b60008060008060008060a087890312156141bb57600080fd5b863567ffffffffffffffff8111156141d257600080fd5b6141de89828a01614090565b9097509550506020870135935060408701356141f981614035565b9250606087013561420981614035565b9150608087013561421981614035565b809150509295509295509295565b60008060006040848603121561423c57600080fd5b833567ffffffffffffffff81111561425357600080fd5b61425f86828701614090565b909450925050602084013561427381614035565b809150509250925092565b600081518084526020808501945080840160005b838110156142c457815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614292565b509495945050505050565b6040815260006142e2604083018561427e565b82810360208481019190915284518083528582019282019060005b81811015614319578451835293830193918301916001016142fd565b5090979650505050505050565b60008060006060848603121561433b57600080fd5b833561434681614035565b9250602084013561435681614035565b9150604084013561427381614035565b6000806000806060858703121561437c57600080fd5b843567ffffffffffffffff81111561439357600080fd5b61439f87828801614090565b9095509350506020850135915060408501356143ba81614035565b939692955090935050565b602081526000614166602083018461427e565b600080600080606085870312156143ee57600080fd5b843567ffffffffffffffff81111561440557600080fd5b61441187828801614090565b909550935050602085013561442581614035565b915060408501356143ba81614035565b6000806040838503121561444857600080fd5b823561445381614035565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff811182821017156144b3576144b3614461565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561450057614500614461565b604052919050565b80356affffffffffffffffffffff8116811461452357600080fd5b919050565b803563ffffffff8116811461452357600080fd5b6000602080838503121561454f57600080fd5b823567ffffffffffffffff8082111561456757600080fd5b818501915085601f83011261457b57600080fd5b81358181111561458d5761458d614461565b61459b848260051b016144b9565b818152848101925060e09182028401850191888311156145ba57600080fd5b938501935b8285101561465e5780858a0312156145d75760008081fd5b6145df614490565b6145e886614508565b8152868601358782015260406145ff818801614528565b9082015260608681013561461281614035565b9082015260808681013561462581614035565b9082015260a08681013561463881614035565b9082015260c08681013561464b81614035565b90820152845293840193928501926145bf565b50979650505050505050565b6000806020838503121561467d57600080fd5b823567ffffffffffffffff81111561469457600080fd5b6146a085828601614090565b90969095509350505050565b6000806000606084860312156146c157600080fd5b83356146cc81614035565b925060208401356146dc81614035565b91506146ea60408501614528565b90509250925092565b60008060008060006060868803121561470b57600080fd5b853561471681614035565b9450602086013567ffffffffffffffff8082111561473357600080fd5b61473f89838a01614090565b9096509450604088013591508082111561475857600080fd5b5061476588828901614090565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156147e7576147e76147a5565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561481e5761481e6147a5565b5060010190565b60006fffffffffffffffffffffffffffffffff8083168181141561484b5761484b6147a5565b6001019392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561488d5761488d6147a5565b500290565b6000826148c8577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000602082840312156148df57600080fd5b5051919050565b600181815b8085111561493f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614925576149256147a5565b8085161561493257918102915b93841c93908002906148eb565b509250929050565b6000826149565750600161075b565b816149635750600061075b565b816001811461497957600281146149835761499f565b600191505061075b565b60ff841115614994576149946147a5565b50506001821b61075b565b5060208310610133831016604e8410600b84101617156149c2575081810a61075b565b6149cc83836148e6565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156149fe576149fe6147a5565b029392505050565b600061416660ff841683614947565b60006141668383614947565b600060208284031215614a3357600080fd5b61416682614508565b600082821015614a4e57614a4e6147a5565b500390565b60008060408385031215614a6657600080fd5b505080516020909101519092909150565b600060208284031215614a8957600080fd5b815160ff8116811461416657600080fd5b600060208284031215614aac57600080fd5b8151801515811461416657600080fd5b60006fffffffffffffffffffffffffffffffff808316818516808303821115614ae757614ae76147a5565b0194935050505056fea26469706673582212208b72c2a3f7280c8e5f94d3a7301662a8a62092f21ab26dafc55e219f7376944264736f6c634300080c0033

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

000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba50000000000000000000000002666951a62d82860e8e1385581e2fb7669097647

-----Decoded View---------------
Arg [0] : _emissionManager (address): 0x749dF84Fd6DE7c0A67db3827e5118259ed3aBBa5
Arg [1] : _staking (address): 0x2666951A62d82860E8e1385581E2FB7669097647

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000749df84fd6de7c0a67db3827e5118259ed3abba5
Arg [1] : 0000000000000000000000002666951a62d82860e8e1385581e2fb7669097647


Block Transaction Gas Used Reward
view all blocks sequenced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.