CELO Price: $0.12 (+1.68%)
Gas: 25 GWei

Contract

0x0007fce3FE6D2F84f2EADDcdaC8e8AA1605eDb5B

Overview

CELO Balance

Celo Mainnet LogoCelo Mainnet LogoCelo Mainnet Logo0 CELO

CELO Value

$0.00

More Info

Private Name Tags

Multichain Info

Transaction Hash
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EpochManager

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.7 <0.8.20;

import "@openzeppelin/contracts8/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts8/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts8/access/Ownable.sol";

import "./interfaces/IOracle.sol";
import "../common/UsingRegistry.sol";

import "../../contracts/common/FixidityLib.sol";
import "../../contracts/common/Initializable.sol";
import "../../contracts/common/interfaces/IEpochManager.sol";
import "../../contracts/common/interfaces/ICeloVersionedContract.sol";
import "./interfaces/IEpochManagerInitializer.sol";

/**
 * @title Contract used for managing CELO L2 epoch and elections.
 * @dev DESIGN_DESICION: we assume that the first epoch on the L2 starts as soon as the system is initialized
 * to minimize amount of "limbo blocks" the network should stop relatively close to an epoch number (but with enough time)
 * to have time to call the function `EpochInitializer.migrateEpochAndValidators()`
 */
contract EpochManager is
  Initializable,
  UsingRegistry,
  IEpochManager,
  ReentrancyGuard,
  ICeloVersionedContract,
  IEpochManagerInitializer
{
  using FixidityLib for FixidityLib.Fraction;

  struct Epoch {
    uint256 firstBlock;
    uint256 lastBlock;
    uint256 startTimestamp;
    uint256 rewardsBlock;
  }

  enum EpochProcessStatus {
    NotStarted,
    Started,
    IndivudualGroupsProcessing
  }

  struct EpochProcessState {
    EpochProcessStatus status;
    uint256 perValidatorReward; // The per validator epoch reward.
    uint256 totalRewardsVoter; // The total rewards to voters.
    uint256 totalRewardsCommunity; // The total community reward.
    uint256 totalRewardsCarbonFund; // The total carbon offsetting partner reward.
  }

  bool public isSystemInitialized;

  // the length of an epoch in seconds
  uint256 public epochDuration;

  uint256 public firstKnownEpoch;
  uint256 internal currentEpochNumber;
  address public oracleAddress;
  address[] public electedAccounts;
  mapping(address => uint256) public processedGroups;

  EpochProcessState public epochProcessing;
  mapping(uint256 => Epoch) internal epochs;
  mapping(address => uint256) public validatorPendingPayments;
  // Electeds in the L1 assumed signers can not change during the epoch
  // so we keep a copy
  address[] public electedSigners;

  uint256 public toProcessGroups = 0;

  /**
   * @notice Event emited when epochProcessing has begun.
   * @param epochNumber The epoch number that is being processed.
   */
  event EpochProcessingStarted(uint256 indexed epochNumber);

  /**
   * @notice Event emited when epochProcessing has ended.
   * @param epochNumber The epoch number that is finished being processed.
   */
  event EpochProcessingEnded(uint256 indexed epochNumber);

  /**
   * @notice Event emited when a new epoch duration is set.
   * @param newEpochDuration The new epoch duration.
   */
  event EpochDurationSet(uint256 indexed newEpochDuration);

  /**
   * @notice Event emited when a new oracle address is set.
   * @param newOracleAddress The new oracle address.
   */
  event OracleAddressSet(address indexed newOracleAddress);

  /**
   * @notice Emitted when an epoch payment is sent.
   * @param validator Address of the validator.
   * @param validatorPayment Amount of cUSD sent to the validator.
   * @param group Address of the validator's group.
   * @param groupPayment Amount of cUSD sent to the group.
   */
  event ValidatorEpochPaymentDistributed(
    address indexed validator,
    uint256 validatorPayment,
    address indexed group,
    uint256 groupPayment,
    address indexed beneficiary,
    uint256 delegatedPayment
  );

  /**
   * @notice Emitted when group is marked for processing.
   * @param group Address of the group to be processed.
   * @param epochNumber The epoch number for which the group gets marked for processing.
   */
  event GroupMarkedForProcessing(address indexed group, uint256 indexed epochNumber);

  /**
   * @notice Emitted when group is processed.
   * @param group Address of the processed group.
   * @param epochNumber The epoch number for which the group gets processed.
   */
  event GroupProcessed(address indexed group, uint256 indexed epochNumber);

  /**
   * @notice Throws if called by other than EpochManagerEnabler contract.
   */
  modifier onlyEpochManagerEnabler() {
    require(
      msg.sender == registry.getAddressForOrDie(EPOCH_MANAGER_ENABLER_REGISTRY_ID),
      "msg.sender is not Enabler"
    );
    _;
  }

  /**
   * @notice Throws if called when EpochManager system has not yet been initalized.
   */
  modifier onlySystemAlreadyInitialized() {
    require(systemAlreadyInitialized(), "Epoch system not initialized");
    _;
  }

  /**
   * @notice Sets initialized == true on implementation contracts
   * @param test Set to true to skip implementation initialization
   */
  constructor(bool test) Initializable(test) {}

  /**
   * @notice Used in place of the constructor to allow the contract to be upgradable via proxy.
   * @param registryAddress The address of the registry core smart contract.
   * @param newEpochDuration The duration of an epoch in seconds.
   */
  function initialize(address registryAddress, uint256 newEpochDuration) external initializer {
    _transferOwnership(msg.sender);
    setRegistry(registryAddress);
    setEpochDuration(newEpochDuration);
    setOracleAddress(registry.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID));
  }

  /**
   * @notice Initializes the EpochManager system, allowing it to start processing epoch
   * and distributing the epoch rewards.
   * @dev Can only be called by the EpochManagerEnabler contract.
   */
  function initializeSystem(
    uint256 firstEpochNumber,
    uint256 firstEpochBlock,
    address[] memory firstElected
  ) external onlyEpochManagerEnabler {
    require(
      getCeloToken().balanceOf(registry.getAddressForOrDie(CELO_UNRELEASED_TREASURY_REGISTRY_ID)) >
        0,
      "CeloUnreleasedTreasury not yet funded."
    );
    require(!systemAlreadyInitialized(), "Epoch system already initialized");
    require(firstEpochNumber > 0, "First epoch number must be greater than 0");
    require(firstEpochBlock > 0, "First epoch block must be greater than 0");
    require(
      firstEpochBlock <= block.number,
      "First epoch block must be less or equal than current block"
    );
    require(firstElected.length > 0, "First elected validators must be greater than 0");
    isSystemInitialized = true;
    firstKnownEpoch = firstEpochNumber;
    currentEpochNumber = firstEpochNumber;

    Epoch storage _currentEpoch = epochs[currentEpochNumber];
    _currentEpoch.firstBlock = firstEpochBlock;
    _currentEpoch.startTimestamp = block.timestamp;

    electedAccounts = firstElected;

    _setElectedSigners(firstElected);
  }

  /**
   * @notice Starts processing an epoch and allocates funds to the beneficiaries.
   * @dev Epoch rewards are frozen at the time of execution.
   * @dev Can only be called once the system is initialized.
   */
  function startNextEpochProcess() external nonReentrant onlySystemAlreadyInitialized {
    require(isTimeForNextEpoch(), "Epoch is not ready to start");
    require(
      epochProcessing.status == EpochProcessStatus.NotStarted,
      "Epoch process is already started"
    );
    require(!isEpochProcessingStarted(), "Epoch process is already started");

    epochProcessing.status = EpochProcessStatus.Started;
    epochs[currentEpochNumber].rewardsBlock = block.number;

    // calculate rewards
    getEpochRewards().updateTargetVotingYield();

    (
      uint256 perValidatorReward,
      uint256 totalRewardsVoter,
      uint256 totalRewardsCommunity,
      uint256 totalRewardsCarbonFund
    ) = getEpochRewards().calculateTargetEpochRewards();

    epochProcessing.perValidatorReward = perValidatorReward;
    epochProcessing.totalRewardsVoter = totalRewardsVoter;
    epochProcessing.totalRewardsCommunity = totalRewardsCommunity;
    epochProcessing.totalRewardsCarbonFund = totalRewardsCarbonFund;

    allocateValidatorsRewards();

    emit EpochProcessingStarted(currentEpochNumber);
  }

  /**
   * @notice Starts individual processing of the elected groups.
   * As second step it is necessary to call processGroup
   */
  function setToProcessGroups() external {
    require(isOnEpochProcess(), "Epoch process is not started");

    EpochProcessState storage _epochProcessing = epochProcessing;
    _epochProcessing.status = EpochProcessStatus.IndivudualGroupsProcessing;

    IValidators validators = getValidators();
    IElection election = getElection();
    IScoreReader scoreReader = getScoreReader();
    require(
      electedAccounts.length == electedSigners.length,
      "Elected accounts and signers of different lengths."
    );
    for (uint i = 0; i < electedAccounts.length; i++) {
      address group = validators.getValidatorsGroup(electedAccounts[i]);
      if (processedGroups[group] == 0) {
        toProcessGroups++;
        uint256 groupScore = scoreReader.getGroupScore(group);
        // We need to precompute epoch rewards for each group since computation depends on total active votes for all groups.
        uint256 epochRewards = election.getGroupEpochRewardsBasedOnScore(
          group,
          _epochProcessing.totalRewardsVoter,
          groupScore
        );
        processedGroups[group] = epochRewards == 0 ? type(uint256).max : epochRewards;
        emit GroupMarkedForProcessing(group, currentEpochNumber);
      }
    }
  }

  /**
   * @notice Processes the rewards for a list of groups. For last group it will also finalize the epoch.
   * @param groups List of validator groups to be processed.
   * @param lessers List of validator groups that hold less votes that indexed group.
   * @param greaters List of validator groups that hold more votes that indexed group.
   */
  function processGroups(
    address[] calldata groups,
    address[] calldata lessers,
    address[] calldata greaters
  ) external {
    for (uint i = 0; i < groups.length; i++) {
      processGroup(groups[i], lessers[i], greaters[i]);
    }
  }

  /**
   * @notice Processes the rewards for a group. For last group it will also finalize the epoch.
   * @param group The group to process.
   * @param lesser The group with less votes than the indexed group.
   * @param greater The group with more votes than the indexed group.
   */
  function processGroup(address group, address lesser, address greater) public {
    EpochProcessState storage _epochProcessing = epochProcessing;
    require(isIndividualProcessing(), "Indivudual epoch process is not started");
    require(toProcessGroups > 0, "no more groups to process");

    uint256 epochRewards = processedGroups[group];
    // checks that group is actually from elected group
    require(epochRewards > 0, "group not from current elected set");
    IElection election = getElection();

    if (epochRewards != type(uint256).max) {
      election.distributeEpochRewards(group, epochRewards, lesser, greater);
    }

    delete processedGroups[group];
    toProcessGroups--;

    emit GroupProcessed(group, currentEpochNumber);

    if (toProcessGroups == 0) {
      _finishEpochHelper(_epochProcessing, election);
    }
  }

  /**
   * @notice Finishes processing an epoch and releasing funds to the beneficiaries.
   * @param groups List of validator groups to be processed.
   * @param lessers List of validator groups that hold less votes that indexed group.
   * @param greaters List of validator groups that hold more votes that indexed group.
   */
  function finishNextEpochProcess(
    address[] calldata groups,
    address[] calldata lessers,
    address[] calldata greaters
  ) external virtual nonReentrant {
    require(isOnEpochProcess(), "Epoch process is not started");
    require(toProcessGroups == 0, "Can't finish epoch while individual groups are being processed");

    EpochProcessState storage _epochProcessing = epochProcessing;

    uint256 _toProcessGroups = 0;
    IValidators validators = getValidators();
    IElection election = getElection();
    IScoreReader scoreReader = getScoreReader();
    require(
      electedAccounts.length == electedSigners.length,
      "Elected accounts and signers of different lengths."
    );
    for (uint i = 0; i < electedAccounts.length; i++) {
      address group = validators.getValidatorsGroup(electedAccounts[i]);
      if (processedGroups[group] == 0) {
        _toProcessGroups++;
        uint256 groupScore = scoreReader.getGroupScore(group);
        // We need to precompute epoch rewards for each group since computation depends on total active votes for all groups.
        uint256 epochRewards = election.getGroupEpochRewardsBasedOnScore(
          group,
          _epochProcessing.totalRewardsVoter,
          groupScore
        );
        processedGroups[group] = epochRewards == 0 ? type(uint256).max : epochRewards;
      }
      delete electedAccounts[i];
      delete electedSigners[i];
    }

    require(_toProcessGroups == groups.length, "number of groups does not match");

    for (uint i = 0; i < groups.length; i++) {
      uint256 epochRewards = processedGroups[groups[i]];
      // checks that group is actually from elected group
      require(epochRewards > 0, "group not from current elected set");
      if (epochRewards != type(uint256).max) {
        election.distributeEpochRewards(groups[i], epochRewards, lessers[i], greaters[i]);
      }

      delete processedGroups[groups[i]];
      emit GroupProcessed(groups[i], currentEpochNumber);
    }

    _finishEpochHelper(_epochProcessing, election);
  }

  /**
   * @notice Sends the allocated epoch payment to a validator, their group, and
   *   delegation beneficiary.
   * @param validator Account of the validator.
   * @dev Can only be called once the system is initialized.
   */
  function sendValidatorPayment(address validator) external onlySystemAlreadyInitialized {
    FixidityLib.Fraction memory totalPayment = FixidityLib.newFixed(
      validatorPendingPayments[validator]
    );
    validatorPendingPayments[validator] = 0;

    IValidators validators = getValidators();
    address group = validators.getValidatorsGroup(validator);
    (, uint256 commissionUnwrapped, , , , , ) = validators.getValidatorGroup(group);

    uint256 groupPayment = totalPayment.multiply(FixidityLib.wrap(commissionUnwrapped)).fromFixed();
    FixidityLib.Fraction memory remainingPayment = FixidityLib.newFixed(
      totalPayment.fromFixed() - groupPayment
    );
    (address beneficiary, uint256 delegatedFraction) = getAccounts().getPaymentDelegation(
      validator
    );
    uint256 delegatedPayment = remainingPayment
      .multiply(FixidityLib.wrap(delegatedFraction))
      .fromFixed();
    uint256 validatorPayment = remainingPayment.fromFixed() - delegatedPayment;

    IERC20 stableToken = IERC20(getStableToken());

    if (validatorPayment > 0) {
      require(stableToken.transfer(validator, validatorPayment), "transfer failed to validator");
    }

    if (groupPayment > 0) {
      require(stableToken.transfer(group, groupPayment), "transfer failed to validator group");
    }

    if (delegatedPayment > 0) {
      require(stableToken.transfer(beneficiary, delegatedPayment), "transfer failed to delegatee");
    }

    emit ValidatorEpochPaymentDistributed(
      validator,
      validatorPayment,
      group,
      groupPayment,
      beneficiary,
      delegatedPayment
    );
  }

  /**
   * @notice Returns the epoch info for the current epoch.
   * @return firstEpoch The first block of the current epoch.
   * @return lastBlock The last block of the current epoch.
   * @return startTimestamp The starting timestamp of the current epoch.
   * @return rewardsBlock The reward block of the current epoch.
   */
  function getCurrentEpoch()
    external
    view
    onlySystemAlreadyInitialized
    returns (uint256, uint256, uint256, uint256)
  {
    return getEpochByNumber(currentEpochNumber);
  }

  /**
   * @return The current epoch number.
   * @dev Can only be called once the system is initialized.
   */
  function getCurrentEpochNumber() external view onlySystemAlreadyInitialized returns (uint256) {
    return currentEpochNumber;
  }

  /**
   * @return The latest epoch processing state.
   */
  function getEpochProcessingState()
    external
    view
    returns (uint256, uint256, uint256, uint256, uint256)
  {
    EpochProcessState storage _epochProcessing = epochProcessing;
    return (
      uint256(_epochProcessing.status),
      _epochProcessing.perValidatorReward,
      _epochProcessing.totalRewardsVoter,
      _epochProcessing.totalRewardsCommunity,
      _epochProcessing.totalRewardsCarbonFund
    );
  }

  /**
   * @notice Used to block select functions in blockable contracts.
   * @return Whether or not the blockable functions are blocked.
   */
  function isBlocked() external view returns (bool) {
    return isEpochProcessingStarted();
  }

  /**
   * @return The number of elected accounts in the current set.
   */
  function numberOfElectedInCurrentSet()
    external
    view
    onlySystemAlreadyInitialized
    returns (uint256)
  {
    return electedAccounts.length;
  }

  /**
   * @return The list of currently elected validators.
   */
  function getElectedAccounts()
    external
    view
    onlySystemAlreadyInitialized
    returns (address[] memory)
  {
    return electedAccounts;
  }

  /**
   * @notice Returns the currently elected account at a specified index.
   * @param index The index of the currently elected account.
   */
  function getElectedAccountByIndex(
    uint256 index
  ) external view onlySystemAlreadyInitialized returns (address) {
    return electedAccounts[index];
  }

  /**
   * @return The list of the validator signers of elected validators.
   */
  function getElectedSigners()
    external
    view
    onlySystemAlreadyInitialized
    returns (address[] memory)
  {
    return electedSigners;
  }

  /**
   * @notice Returns the currently elected signer address at a specified index.
   * @param index The index of the currently elected signer.
   */
  function getElectedSignerByIndex(
    uint256 index
  ) external view onlySystemAlreadyInitialized returns (address) {
    return electedSigners[index];
  }

  /**
   * @param epoch The epoch number of interest.
   * @return The First block of the specified epoch.
   */
  function getFirstBlockAtEpoch(uint256 epoch) external view returns (uint256) {
    require(epoch >= firstKnownEpoch, "Epoch not known");
    require(epoch <= currentEpochNumber, "Epoch not created yet");
    return epochs[epoch].firstBlock;
  }

  /**
   * @param epoch The epoch number of interest.
   * @return The last block of the specified epoch.
   */
  function getLastBlockAtEpoch(uint256 epoch) external view returns (uint256) {
    require(epoch >= firstKnownEpoch, "Epoch not known");
    require(epoch < currentEpochNumber, "Epoch not finished yet");
    return epochs[epoch].lastBlock;
  }

  /**
   * @notice Returns the epoch number of a specified blockNumber.
   * @param _blockNumber Block number of the epoch info is retreived.
   */
  function getEpochNumberOfBlock(
    uint256 _blockNumber
  ) external view onlySystemAlreadyInitialized returns (uint256) {
    (uint256 _epochNumber, , , , ) = _getEpochByBlockNumber(_blockNumber);
    return _epochNumber;
  }

  /**
   * @notice Returns the epoch info of a specified blockNumber.
   * @param _blockNumber Block number of the epoch info is retreived.
   * @return firstEpoch The first block of the given block number.
   * @return lastBlock The first block of the given block number.
   * @return startTimestamp The starting timestamp of the given block number.
   * @return rewardsBlock The reward block of the given block number.
   */
  function getEpochByBlockNumber(
    uint256 _blockNumber
  ) external view onlySystemAlreadyInitialized returns (uint256, uint256, uint256, uint256) {
    (
      ,
      uint256 _firstBlock,
      uint256 _lastBlock,
      uint256 _startTimestamp,
      uint256 _rewardsBlock
    ) = _getEpochByBlockNumber(_blockNumber);
    return (_firstBlock, _lastBlock, _startTimestamp, _rewardsBlock);
  }

  /**
   * @notice Returns the storage, major, minor, and patch version of the contract.
   * @return Storage version of the contract.
   * @return Major version of the contract.
   * @return Minor version of the contract.
   * @return Patch version of the contract.
   */
  function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) {
    return (1, 1, 0, 0);
  }

  /**
   * @notice Sets the time duration of an epoch.
   * @param newEpochDuration The duration of an epoch in seconds.
   * @dev Can only be set by owner.
   */
  function setEpochDuration(uint256 newEpochDuration) public onlyOwner {
    require(newEpochDuration > 0, "New epoch duration must be greater than zero.");
    require(!isOnEpochProcess(), "Cannot change epoch duration during processing.");
    epochDuration = newEpochDuration;
    emit EpochDurationSet(newEpochDuration);
  }

  /**
   * @notice Sets the address of the Oracle used by this contract.
   * @param newOracleAddress The address of the new oracle.
   * @dev Can only be set by owner.
   */
  function setOracleAddress(address newOracleAddress) public onlyOwner {
    require(newOracleAddress != address(0), "Cannot set address zero as the Oracle.");
    require(newOracleAddress != oracleAddress, "Oracle address cannot be the same.");
    require(!isOnEpochProcess(), "Cannot change oracle address during epoch processing.");
    oracleAddress = newOracleAddress;
    emit OracleAddressSet(newOracleAddress);
  }

  /**
   * @return Whether epoch is being processed by individualy group by group.
   */
  function isIndividualProcessing() public view returns (bool) {
    return epochProcessing.status == EpochProcessStatus.IndivudualGroupsProcessing;
  }

  /**
   * @return Whether epoch process has been started.
   */
  function isEpochProcessingStarted() public view returns (bool) {
    return isOnEpochProcess() || isIndividualProcessing();
  }

  /**
   * @return Whether or not the next epoch can be processed.
   */
  function isTimeForNextEpoch() public view returns (bool) {
    return block.timestamp >= epochs[currentEpochNumber].startTimestamp + epochDuration;
  }

  /**
   * @return Whether or not the current epoch is being processed.
   */
  function isOnEpochProcess() public view returns (bool) {
    return epochProcessing.status == EpochProcessStatus.Started;
  }

  /**
   * @return Whether or not the EpochManager contract has been activated to start processing epochs.
   */
  function systemAlreadyInitialized() public view returns (bool) {
    return initialized && isSystemInitialized;
  }

  /**
   * @notice Returns the epoch info of a specified epoch.
   * @param epochNumber Epoch number where the epoch info is retreived.
   * @return firstEpoch The first block of the given epoch.
   * @return lastBlock The first block of the given epoch.
   * @return startTimestamp The starting timestamp of the given epoch.
   * @return rewardsBlock The reward block of the given epoch.
   */
  function getEpochByNumber(
    uint256 epochNumber
  ) public view onlySystemAlreadyInitialized returns (uint256, uint256, uint256, uint256) {
    Epoch memory _epoch = epochs[epochNumber];
    return (_epoch.firstBlock, _epoch.lastBlock, _epoch.startTimestamp, _epoch.rewardsBlock);
  }

  /**
   * @notice Allocates rewards to elected validator accounts.
   */
  function allocateValidatorsRewards() internal {
    uint256 totalRewards = 0;
    IScoreReader scoreReader = getScoreReader();
    IValidators validators = getValidators();

    EpochProcessState storage _epochProcessing = epochProcessing;

    for (uint i = 0; i < electedAccounts.length; i++) {
      uint256 validatorScore = scoreReader.getValidatorScore(electedAccounts[i]);
      uint256 validatorReward = validators.computeEpochReward(
        electedAccounts[i],
        validatorScore,
        _epochProcessing.perValidatorReward
      );
      validatorPendingPayments[electedAccounts[i]] += validatorReward;
      totalRewards += validatorReward;
    }
    if (totalRewards == 0) {
      return;
    }

    // Mint all cUSD required for payment and the corresponding CELO
    validators.mintStableToEpochManager(totalRewards);

    (uint256 numerator, uint256 denominator) = IOracle(oracleAddress).getExchangeRate(
      address(getStableToken())
    );

    uint256 CELOequivalent = (numerator * totalRewards) / denominator;
    getCeloUnreleasedTreasury().release(
      registry.getAddressForOrDie(RESERVE_REGISTRY_ID),
      CELOequivalent
    );
  }

  /**
   * @notice Updates the list of elected validator signers.
   */
  function _setElectedSigners(address[] memory _elected) internal {
    require(electedAccounts.length > 0, "Elected list length cannot be zero.");
    IAccounts accounts = getAccounts();
    electedSigners = new address[](_elected.length);
    for (uint i = 0; i < _elected.length; i++) {
      electedSigners[i] = accounts.getValidatorSigner(_elected[i]);
    }
  }

  /**
   * @notice Finishes processing an epoch and releasing funds to the beneficiaries.
   * @param _epochProcessing The current epoch processing state.
   * @param election The Election contract.
   */
  function _finishEpochHelper(
    EpochProcessState storage _epochProcessing,
    IElection election
  ) internal {
    // finalize epoch
    // last block should be the block before and timestamp from previous block
    epochs[currentEpochNumber].lastBlock = block.number - 1;
    currentEpochNumber++;
    // start new epoch
    epochs[currentEpochNumber].firstBlock = block.number;
    epochs[currentEpochNumber].startTimestamp = block.timestamp;

    // run elections
    address[] memory _newlyElected = election.electValidatorAccounts();
    electedAccounts = _newlyElected;
    _setElectedSigners(_newlyElected);

    ICeloUnreleasedTreasury celoUnreleasedTreasury = getCeloUnreleasedTreasury();
    celoUnreleasedTreasury.release(
      registry.getAddressForOrDie(GOVERNANCE_REGISTRY_ID),
      _epochProcessing.totalRewardsCommunity
    );
    celoUnreleasedTreasury.release(
      getEpochRewards().carbonOffsettingPartner(),
      _epochProcessing.totalRewardsCarbonFund
    );

    _epochProcessing.status = EpochProcessStatus.NotStarted;
    _epochProcessing.perValidatorReward = 0;
    _epochProcessing.totalRewardsVoter = 0;
    _epochProcessing.totalRewardsCommunity = 0;
    _epochProcessing.totalRewardsCarbonFund = 0;

    emit EpochProcessingEnded(currentEpochNumber - 1);
  }

  /**
   * @notice Returns the epoch info of a specified blockNumber.
   * @dev This function is here for backward compatibility. It is rather gas heavy and can run out of gas.
   * @param _blockNumber Block number of the epoch info is retreived.
   * @return firstEpoch The first block of the given block number.
   * @return lastBlock The first block of the given block number.
   * @return startTimestamp The starting timestamp of the given block number.
   * @return rewardsBlock The reward block of the given block number.
   */
  function _getEpochByBlockNumber(
    uint256 _blockNumber
  )
    internal
    view
    onlySystemAlreadyInitialized
    returns (uint256, uint256, uint256, uint256, uint256)
  {
    require(_blockNumber <= block.number, "Invalid blockNumber. Value too high.");

    (uint256 _firstBlockOfFirstEpoch, , , ) = getEpochByNumber(firstKnownEpoch);

    require(_blockNumber >= _firstBlockOfFirstEpoch, "Invalid blockNumber. Value too low.");

    uint256 _firstBlockOfCurrentEpoch = epochs[currentEpochNumber].firstBlock;

    if (_blockNumber >= _firstBlockOfCurrentEpoch) {
      (
        uint256 _firstBlock,
        uint256 _lastBlock,
        uint256 _startTimestamp,
        uint256 _rewardsBlock
      ) = getEpochByNumber(currentEpochNumber);
      return (currentEpochNumber, _firstBlock, _lastBlock, _startTimestamp, _rewardsBlock);
    }

    uint256 left = firstKnownEpoch;
    uint256 right = currentEpochNumber - 1;

    while (left <= right) {
      uint256 mid = (left + right) / 2;
      uint256 _epochFirstBlock = epochs[mid].firstBlock;
      uint256 _epochLastBlock = epochs[mid].lastBlock;

      if (_blockNumber >= _epochFirstBlock && _blockNumber <= _epochLastBlock) {
        Epoch memory _epoch = epochs[mid];
        return (
          mid,
          _epoch.firstBlock,
          _epoch.lastBlock,
          _epoch.startTimestamp,
          _epoch.rewardsBlock
        );
      } else if (_blockNumber < _epochFirstBlock) {
        right = mid - 1;
      } else {
        left = mid + 1;
      }
    }

    revert("No matching epoch found for the given block number.");
  }
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface ISortedOracles {
  function addOracle(address, address) external;
  function removeOracle(address, address, uint256) external;
  function report(address, uint256, address, address) external;
  function removeExpiredReports(address, uint256) external;
  function isOldestReportExpired(address token) external view returns (bool, address);
  function numRates(address) external view returns (uint256);
  function medianRate(address) external view returns (uint256, uint256);
  function numTimestamps(address) external view returns (uint256);
  function medianTimestamp(address) external view returns (uint256);
}

File 3 of 26 : IValidators.sol
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IValidators {
  function registerValidator(
    bytes calldata,
    bytes calldata,
    bytes calldata
  ) external returns (bool);
  function registerValidatorNoBls(bytes calldata ecdsaPublicKey) external returns (bool);
  function deregisterValidator(uint256) external returns (bool);
  function affiliate(address) external returns (bool);
  function deaffiliate() external returns (bool);
  function updateBlsPublicKey(bytes calldata, bytes calldata) external returns (bool);
  function registerValidatorGroup(uint256) external returns (bool);
  function deregisterValidatorGroup(uint256) external returns (bool);
  function addMember(address) external returns (bool);
  function addFirstMember(address, address, address) external returns (bool);
  function removeMember(address) external returns (bool);
  function reorderMember(address, address, address) external returns (bool);
  function updateCommission() external;
  function setNextCommissionUpdate(uint256) external;
  function resetSlashingMultiplier() external;

  // only owner
  function setCommissionUpdateDelay(uint256) external;
  function setMaxGroupSize(uint256) external returns (bool);
  function setMembershipHistoryLength(uint256) external returns (bool);
  function setValidatorScoreParameters(uint256, uint256) external returns (bool);
  function setGroupLockedGoldRequirements(uint256, uint256) external returns (bool);
  function setValidatorLockedGoldRequirements(uint256, uint256) external returns (bool);
  function setSlashingMultiplierResetPeriod(uint256) external;
  function setDowntimeGracePeriod(uint256 value) external;

  // only registered contract
  function updateEcdsaPublicKey(address, address, bytes calldata) external returns (bool);
  function updatePublicKeys(
    address,
    address,
    bytes calldata,
    bytes calldata,
    bytes calldata
  ) external returns (bool);
  function mintStableToEpochManager(uint256 amount) external;

  // only VM
  function updateValidatorScoreFromSigner(address, uint256) external;
  function distributeEpochPaymentsFromSigner(address, uint256) external returns (uint256);

  // only slasher
  function forceDeaffiliateIfValidator(address) external;
  function halveSlashingMultiplier(address) external;

  // view functions
  function maxGroupSize() external view returns (uint256);
  function downtimeGracePeriod() external view returns (uint256);
  function getCommissionUpdateDelay() external view returns (uint256);
  function getValidatorScoreParameters() external view returns (uint256, uint256);
  function getMembershipHistory(
    address
  ) external view returns (uint256[] memory, address[] memory, uint256, uint256);
  function calculateEpochScore(uint256) external view returns (uint256);
  function calculateGroupEpochScore(uint256[] calldata) external view returns (uint256);
  function getAccountLockedGoldRequirement(address) external view returns (uint256);
  function meetsAccountLockedGoldRequirements(address) external view returns (bool);
  function getValidatorBlsPublicKeyFromSigner(address) external view returns (bytes memory);
  function getValidator(
    address account
  ) external view returns (bytes memory, bytes memory, address, uint256, address);
  function getValidatorsGroup(address account) external view returns (address affiliation);
  function getValidatorGroup(
    address
  )
    external
    view
    returns (address[] memory, uint256, uint256, uint256, uint256[] memory, uint256, uint256);
  function getGroupNumMembers(address) external view returns (uint256);
  function getTopGroupValidators(address, uint256) external view returns (address[] memory);
  function getTopGroupValidatorsAccounts(address, uint256) external view returns (address[] memory);
  function getGroupsNumMembers(
    address[] calldata accounts
  ) external view returns (uint256[] memory);
  function getNumRegisteredValidators() external view returns (uint256);
  function groupMembershipInEpoch(address, uint256, uint256) external view returns (address);

  function getValidatorLockedGoldRequirements() external view returns (uint256, uint256);
  function getGroupLockedGoldRequirements() external view returns (uint256, uint256);
  function getRegisteredValidators() external view returns (address[] memory);
  function getRegisteredValidatorGroups() external view returns (address[] memory);
  function isValidatorGroup(address) external view returns (bool);
  function isValidator(address) external view returns (bool);
  function getValidatorGroupSlashingMultiplier(address) external view returns (uint256);
  function getMembershipInLastEpoch(address) external view returns (address);
  function getMembershipInLastEpochFromSigner(address) external view returns (address);
  function computeEpochReward(
    address account,
    uint256 score,
    uint256 maxPayment
  ) external view returns (uint256);
  function getMembershipHistoryLength() external view returns (uint256);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface ILockedGold {
  function lock() external payable;
  function incrementNonvotingAccountBalance(address, uint256) external;
  function decrementNonvotingAccountBalance(address, uint256) external;

  function unlock(uint256) external;
  function relock(uint256, uint256) external;
  function withdraw(uint256) external;
  function slash(
    address account,
    uint256 penalty,
    address reporter,
    uint256 reward,
    address[] calldata lessers,
    address[] calldata greaters,
    uint256[] calldata indices
  ) external;
  function addSlasher(string calldata slasherIdentifier) external;

  function getAccountTotalLockedGold(address) external view returns (uint256);
  function getTotalLockedGold() external view returns (uint256);
  function getPendingWithdrawals(
    address
  ) external view returns (uint256[] memory, uint256[] memory);
  function getPendingWithdrawal(
    address account,
    uint256 index
  ) external view returns (uint256, uint256);
  function getTotalPendingWithdrawals(address) external view returns (uint256);
  function isSlasher(address) external view returns (bool);

  function getAccountTotalDelegatedFraction(address account) external view returns (uint256);

  function getAccountTotalGovernanceVotingPower(address account) external view returns (uint256);
  function unlockingPeriod() external view returns (uint256);
  function getAccountNonvotingLockedGold(address account) external view returns (uint256);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface ILockedCelo {
  function lock() external payable;
  function incrementNonvotingAccountBalance(address, uint256) external;
  function decrementNonvotingAccountBalance(address, uint256) external;

  function unlock(uint256) external;
  function relock(uint256, uint256) external;
  function withdraw(uint256) external;
  function slash(
    address account,
    uint256 penalty,
    address reporter,
    uint256 reward,
    address[] calldata lessers,
    address[] calldata greaters,
    uint256[] calldata indices
  ) external;
  function addSlasher(string calldata slasherIdentifier) external;

  function getAccountNonvotingLockedGold(address account) external view returns (uint256);
  function getAccountTotalLockedCelo(address) external view returns (uint256);
  function getTotalLockedCelo() external view returns (uint256);
  function getPendingWithdrawals(
    address
  ) external view returns (uint256[] memory, uint256[] memory);
  function getPendingWithdrawal(
    address account,
    uint256 index
  ) external view returns (uint256, uint256);
  function getTotalPendingWithdrawals(address) external view returns (uint256);
  function isSlasher(address) external view returns (bool);

  function getAccountTotalDelegatedFraction(address account) external view returns (uint256);

  function getAccountTotalGovernanceVotingPower(address account) external view returns (uint256);
  function unlockingPeriod() external view returns (uint256);
  function getAccountNonvotingLockedCelo(address account) external view returns (uint256);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IGovernance {
  function removeVotesWhenRevokingDelegatedVotes(
    address account,
    uint256 maxAmountAllowed
  ) external;
  function votePartially(
    uint256 proposalId,
    uint256 index,
    uint256 yesVotes,
    uint256 noVotes,
    uint256 abstainVotes
  ) external returns (bool);

  function setConstitution(address destination, bytes4 functionId, uint256 threshold) external;

  function isVoting(address) external view returns (bool);
  function getAmountOfGoldUsedForVoting(address account) external view returns (uint256);

  function getProposal(
    uint256 proposalId
  ) external view returns (address, uint256, uint256, uint256, string memory, uint256, bool);

  function getReferendumStageDuration() external view returns (uint256);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IEpochRewards {
  function updateTargetVotingYield() external;
  function isReserveLow() external view returns (bool);
  function calculateTargetEpochRewards() external view returns (uint256, uint256, uint256, uint256);
  function getTargetVotingYieldParameters() external view returns (uint256, uint256, uint256);
  function getRewardsMultiplierParameters() external view returns (uint256, uint256, uint256);
  function getCommunityRewardFraction() external view returns (uint256);
  function getCarbonOffsettingFraction() external view returns (uint256);
  function getTargetVotingGoldFraction() external view returns (uint256);
  function getRewardsMultiplier() external view returns (uint256);
  function carbonOffsettingPartner() external view returns (address);
}

File 8 of 26 : IElection.sol
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IElection {
  function vote(address, uint256, address, address) external returns (bool);
  function activate(address) external returns (bool);
  function revokeActive(address, uint256, address, address, uint256) external returns (bool);
  function revokeAllActive(address, address, address, uint256) external returns (bool);
  function revokePending(address, uint256, address, address, uint256) external returns (bool);
  function markGroupIneligible(address) external;
  function markGroupEligible(address, address, address) external;
  function allowedToVoteOverMaxNumberOfGroups(address) external returns (bool);
  function forceDecrementVotes(
    address,
    uint256,
    address[] calldata,
    address[] calldata,
    uint256[] calldata
  ) external returns (uint256);
  function setAllowedToVoteOverMaxNumberOfGroups(bool flag) external;

  // only owner
  function setElectableValidators(uint256, uint256) external returns (bool);
  function setMaxNumGroupsVotedFor(uint256) external returns (bool);
  function setElectabilityThreshold(uint256) external returns (bool);

  // only VM
  function distributeEpochRewards(address, uint256, address, address) external;

  // view functions
  function electValidatorSigners() external view returns (address[] memory);
  function electValidatorAccounts() external view returns (address[] memory);
  function electNValidatorSigners(uint256, uint256) external view returns (address[] memory);
  function electNValidatorAccounts(uint256, uint256) external view returns (address[] memory);
  function getElectableValidators() external view returns (uint256, uint256);
  function getElectabilityThreshold() external view returns (uint256);
  function getNumVotesReceivable(address) external view returns (uint256);
  function getTotalVotes() external view returns (uint256);
  function getActiveVotes() external view returns (uint256);
  function getTotalVotesByAccount(address) external view returns (uint256);
  function getPendingVotesForGroupByAccount(address, address) external view returns (uint256);
  function getActiveVotesForGroupByAccount(address, address) external view returns (uint256);
  function getTotalVotesForGroupByAccount(address, address) external view returns (uint256);
  function getActiveVoteUnitsForGroupByAccount(address, address) external view returns (uint256);
  function getTotalVotesForGroup(address) external view returns (uint256);
  function getActiveVotesForGroup(address) external view returns (uint256);
  function getPendingVotesForGroup(address) external view returns (uint256);
  function getGroupEligibility(address) external view returns (bool);
  function getGroupEpochRewards(
    address,
    uint256,
    uint256[] calldata
  ) external view returns (uint256);
  function getGroupEpochRewardsBasedOnScore(
    address group,
    uint256 totalEpochRewards,
    uint256 groupScore
  ) external view returns (uint256);
  function getGroupsVotedForByAccount(address) external view returns (address[] memory);
  function getEligibleValidatorGroups() external view returns (address[] memory);
  function getTotalVotesForEligibleValidatorGroups()
    external
    view
    returns (address[] memory, uint256[] memory);
  function getCurrentValidatorSigners() external view returns (address[] memory);
  function canReceiveVotes(address, uint256) external view returns (bool);
  function hasActivatablePendingVotes(address, address) external view returns (bool);
  function validatorSignerAddressFromCurrentSet(uint256 index) external view returns (address);
  function numberValidatorsInCurrentSet() external view returns (uint256);
  function owner() external view returns (address);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IRegistry {
  function setAddressFor(string calldata, address) external;
  function getAddressForOrDie(bytes32) external view returns (address);
  function getAddressFor(bytes32) external view returns (address);
  function getAddressForStringOrDie(string calldata identifier) external view returns (address);
  function getAddressForString(string calldata identifier) external view returns (address);
  function isOneOf(bytes32[] calldata, address) external view returns (bool);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IFreezer {
  function freeze(address target) external;
  function unfreeze(address target) external;
  function isFrozen(address) external view returns (bool);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IFeeHandlerSeller {
  function sell(
    address sellTokenAddress,
    address buyTokenAddress,
    uint256 amount,
    uint256 minAmount
  ) external returns (uint256);
  // in case some funds need to be returned or moved to another contract
  function transfer(address token, uint256 amount, address to) external returns (bool);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IFeeCurrencyWhitelist {
  function initialize() external;
  function addToken(address) external;
  function getWhitelist() external view returns (address[] memory);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IEpochManager {
  function initializeSystem(
    uint256 firstEpochNumber,
    uint256 firstEpochBlock,
    address[] calldata firstElected
  ) external;
  function startNextEpochProcess() external;
  function finishNextEpochProcess(
    address[] calldata groups,
    address[] calldata lessers,
    address[] calldata greaters
  ) external;
  function setToProcessGroups() external;
  function processGroup(address group, address lesser, address greater) external;
  function sendValidatorPayment(address) external;
  function getCurrentEpoch() external view returns (uint256, uint256, uint256, uint256);
  function getEpochByNumber(
    uint256 epochNumber
  ) external view returns (uint256, uint256, uint256, uint256);
  function getEpochByBlockNumber(
    uint256 blockNumber
  ) external view returns (uint256, uint256, uint256, uint256);
  function getEpochNumberOfBlock(uint256) external view returns (uint256);
  function getCurrentEpochNumber() external view returns (uint256);
  function numberOfElectedInCurrentSet() external view returns (uint256);
  function getElectedAccounts() external view returns (address[] memory);
  function getElectedAccountByIndex(uint256 index) external view returns (address);
  function getElectedSigners() external view returns (address[] memory);
  function getElectedSignerByIndex(uint256 index) external view returns (address);
  function epochDuration() external view returns (uint256);
  function firstKnownEpoch() external view returns (uint256);
  function getEpochProcessingState()
    external
    view
    returns (uint256, uint256, uint256, uint256, uint256);
  function systemAlreadyInitialized() external view returns (bool);
  function isBlocked() external view returns (bool);
  function isTimeForNextEpoch() external view returns (bool);
  function isOnEpochProcess() external view returns (bool);
  function getFirstBlockAtEpoch(uint256) external view returns (uint256);
  function getLastBlockAtEpoch(uint256) external view returns (uint256);
}

File 14 of 26 : ICeloVersionedContract.sol
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface ICeloVersionedContract {
  /**
   * @notice Returns the storage, major, minor, and patch version of the contract.
   * @return Storage version of the contract.
   * @return Major version of the contract.
   * @return Minor version of the contract.
   * @return Patch version of the contract.
   */
  function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface ICeloUnreleasedTreasury {
  /**
   * @notice Releases the Celo to the specified address.
   * @param to The address to release the amount to.
   * @param amount The amount to release.
   */
  function release(address to, uint256 amount) external;

  function getRemainingBalanceToRelease() external view returns (uint256);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IAccounts {
  function setAccountDataEncryptionKey(bytes calldata) external;
  function setMetadataURL(string calldata) external;
  function setName(string calldata) external;
  function setWalletAddress(address, uint8, bytes32, bytes32) external;
  function setAccount(string calldata, bytes calldata, address, uint8, bytes32, bytes32) external;

  function authorizeVoteSigner(address, uint8, bytes32, bytes32) external;
  function authorizeValidatorSigner(address, uint8, bytes32, bytes32) external;
  function authorizeValidatorSignerWithPublicKey(
    address,
    uint8,
    bytes32,
    bytes32,
    bytes calldata
  ) external;
  function authorizeValidatorSignerWithKeys(
    address,
    uint8,
    bytes32,
    bytes32,
    bytes calldata,
    bytes calldata,
    bytes calldata
  ) external;
  function authorizeAttestationSigner(address, uint8, bytes32, bytes32) external;
  function setEip712DomainSeparator() external;
  function createAccount() external returns (bool);

  function setPaymentDelegation(address, uint256) external;

  function isAccount(address) external view returns (bool);
  function voteSignerToAccount(address) external view returns (address);
  function validatorSignerToAccount(address) external view returns (address);
  function attestationSignerToAccount(address) external view returns (address);
  function signerToAccount(address) external view returns (address);
  function getAttestationSigner(address) external view returns (address);
  function getValidatorSigner(address) external view returns (address);
  function getVoteSigner(address) external view returns (address);
  function hasAuthorizedVoteSigner(address) external view returns (bool);
  function hasAuthorizedValidatorSigner(address) external view returns (bool);
  function hasAuthorizedAttestationSigner(address) external view returns (bool);

  function batchGetMetadataURL(
    address[] calldata
  ) external view returns (uint256[] memory, bytes memory);

  function getDataEncryptionKey(address) external view returns (bytes memory);
  function getWalletAddress(address) external view returns (address);
  function getMetadataURL(address) external view returns (string memory);

  function getName(address) external view returns (string memory);

  function getPaymentDelegation(address) external view returns (address, uint256);
  function isSigner(address, address, bytes32) external view returns (bool);
}

File 17 of 26 : Initializable.sol
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

/**
 * @title Used with proxied contracts that have an `initialize` function.
 * @notice Ensures the `initialize` function:
 *         - gets called only once
 *         - cannot be called on the logic contract.
 */
contract Initializable {
  bool public initialized;

  /**
   * @notice Ensures the initializer function cannot be called more than once.
   */
  modifier initializer() {
    require(!initialized, "contract already initialized");
    initialized = true;
    _;
  }

  /**
   * @notice By default, ensures that the `initialize` function cannot be called
   * on the logic contract.
   * @param testingDeployment When set to true, allows the `initialize` function
   * to be called, which is useful in testing when not setting up with a Proxy.
   */
  constructor(bool testingDeployment) public {
    if (!testingDeployment) {
      initialized = true;
    }
  }
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

/**
 * @title FixidityLib
 * @author Gadi Guy, Alberto Cuesta Canada
 * @notice This library provides fixed point arithmetic with protection against
 * overflow.
 * All operations are done with uint256 and the operands must have been created
 * with any of the newFrom* functions, which shift the comma digits() to the
 * right and check for limits, or with wrap() which expects a number already
 * in the internal representation of a fraction.
 * When using this library be sure to use maxNewFixed() as the upper limit for
 * creation of fixed point numbers.
 * @dev All contained functions are pure and thus marked internal to be inlined
 * on consuming contracts at compile time for gas efficiency.
 */
library FixidityLib {
  struct Fraction {
    uint256 value;
  }

  uint256 private constant FIXED1_UINT = 1000000000000000000000000;

  /**
   * @notice Number of positions that the comma is shifted to the right.
   */
  function digits() internal pure returns (uint8) {
    return 24;
  }

  /**
   * @notice This is 1 in the fixed point units used in this library.
   * @dev Test fixed1() equals 10^digits()
   * Hardcoded to 24 digits.
   */
  function fixed1() internal pure returns (Fraction memory) {
    return Fraction(FIXED1_UINT);
  }

  /**
   * @notice Wrap a uint256 that represents a 24-decimal fraction in a Fraction
   * struct.
   * @param x Number that already represents a 24-decimal fraction.
   * @return A Fraction struct with contents x.
   */
  function wrap(uint256 x) internal pure returns (Fraction memory) {
    return Fraction(x);
  }

  /**
   * @notice Unwraps the uint256 inside of a Fraction struct.
   */
  function unwrap(Fraction memory x) internal pure returns (uint256) {
    return x.value;
  }

  /**
   * @notice The amount of decimals lost on each multiplication operand.
   * @dev Test mulPrecision() equals sqrt(fixed1)
   */
  function mulPrecision() internal pure returns (uint256) {
    return 1000000000000;
  }

  /**
   * @notice Maximum value that can be converted to fixed point. Optimize for deployment.
   * @dev
   * Test maxNewFixed() equals maxUint256() / fixed1()
   */
  function maxNewFixed() internal pure returns (uint256) {
    return 115792089237316195423570985008687907853269984665640564;
  }

  /**
   * @notice Converts a uint256 to fixed point Fraction
   * @dev Test newFixed(0) returns 0
   * Test newFixed(1) returns fixed1()
   * Test newFixed(maxNewFixed()) returns maxNewFixed() * fixed1()
   * Test newFixed(maxNewFixed()+1) fails
   */
  function newFixed(uint256 x) internal pure returns (Fraction memory) {
    require(x <= maxNewFixed(), "can't create fixidity number larger than maxNewFixed()");
    return Fraction(x * FIXED1_UINT);
  }

  /**
   * @notice Converts a uint256 in the fixed point representation of this
   * library to a non decimal. All decimal digits will be truncated.
   */
  function fromFixed(Fraction memory x) internal pure returns (uint256) {
    return x.value / FIXED1_UINT;
  }

  /**
   * @notice Converts two uint256 representing a fraction to fixed point units,
   * equivalent to multiplying dividend and divisor by 10^digits().
   * @param numerator numerator must be <= maxNewFixed()
   * @param denominator denominator must be <= maxNewFixed() and denominator can't be 0
   * @dev
   * Test newFixedFraction(1,0) fails
   * Test newFixedFraction(0,1) returns 0
   * Test newFixedFraction(1,1) returns fixed1()
   * Test newFixedFraction(1,fixed1()) returns 1
   */
  function newFixedFraction(
    uint256 numerator,
    uint256 denominator
  ) internal pure returns (Fraction memory) {
    Fraction memory convertedNumerator = newFixed(numerator);
    Fraction memory convertedDenominator = newFixed(denominator);
    return divide(convertedNumerator, convertedDenominator);
  }

  /**
   * @notice Returns the integer part of a fixed point number.
   * @dev
   * Test integer(0) returns 0
   * Test integer(fixed1()) returns fixed1()
   * Test integer(newFixed(maxNewFixed())) returns maxNewFixed()*fixed1()
   */
  function integer(Fraction memory x) internal pure returns (Fraction memory) {
    return Fraction((x.value / FIXED1_UINT) * FIXED1_UINT); // Can't overflow
  }

  /**
   * @notice Returns the fractional part of a fixed point number.
   * In the case of a negative number the fractional is also negative.
   * @dev
   * Test fractional(0) returns 0
   * Test fractional(fixed1()) returns 0
   * Test fractional(fixed1()-1) returns 10^24-1
   */
  function fractional(Fraction memory x) internal pure returns (Fraction memory) {
    return Fraction(x.value - (x.value / FIXED1_UINT) * FIXED1_UINT); // Can't overflow
  }

  /**
   * @notice x+y.
   * @dev The maximum value that can be safely used as an addition operator is defined as
   * maxFixedAdd = maxUint256()-1 / 2, or
   * 57896044618658097711785492504343953926634992332820282019728792003956564819967.
   * Test add(maxFixedAdd,maxFixedAdd) equals maxFixedAdd + maxFixedAdd
   * Test add(maxFixedAdd+1,maxFixedAdd+1) throws
   */
  function add(Fraction memory x, Fraction memory y) internal pure returns (Fraction memory) {
    uint256 z = x.value + y.value;
    require(z >= x.value, "add overflow detected");
    return Fraction(z);
  }

  /**
   * @notice x-y.
   * @dev
   * Test subtract(6, 10) fails
   */
  function subtract(Fraction memory x, Fraction memory y) internal pure returns (Fraction memory) {
    require(x.value >= y.value, "substraction underflow detected");
    return Fraction(x.value - y.value);
  }

  /**
   * @notice x*y. If any of the operators is higher than the max multiplier value it
   * might overflow.
   * @dev The maximum value that can be safely used as a multiplication operator
   * (maxFixedMul) is calculated as sqrt(maxUint256()*fixed1()),
   * or 340282366920938463463374607431768211455999999999999
   * Test multiply(0,0) returns 0
   * Test multiply(maxFixedMul,0) returns 0
   * Test multiply(0,maxFixedMul) returns 0
   * Test multiply(fixed1()/mulPrecision(),fixed1()*mulPrecision()) returns fixed1()
   * Test multiply(maxFixedMul,maxFixedMul) is around maxUint256()
   * Test multiply(maxFixedMul+1,maxFixedMul+1) fails
   */
  function multiply(Fraction memory x, Fraction memory y) internal pure returns (Fraction memory) {
    if (x.value == 0 || y.value == 0) return Fraction(0);
    if (y.value == FIXED1_UINT) return x;
    if (x.value == FIXED1_UINT) return y;

    // Separate into integer and fractional parts
    // x = x1 + x2, y = y1 + y2
    uint256 x1 = integer(x).value / FIXED1_UINT;
    uint256 x2 = fractional(x).value;
    uint256 y1 = integer(y).value / FIXED1_UINT;
    uint256 y2 = fractional(y).value;

    // (x1 + x2) * (y1 + y2) = (x1 * y1) + (x1 * y2) + (x2 * y1) + (x2 * y2)
    uint256 x1y1 = x1 * y1;
    if (x1 != 0) require(x1y1 / x1 == y1, "overflow x1y1 detected");

    // x1y1 needs to be multiplied back by fixed1
    // solium-disable-next-line mixedcase
    uint256 fixed_x1y1 = x1y1 * FIXED1_UINT;
    if (x1y1 != 0) require(fixed_x1y1 / x1y1 == FIXED1_UINT, "overflow x1y1 * fixed1 detected");
    x1y1 = fixed_x1y1;

    uint256 x2y1 = x2 * y1;
    if (x2 != 0) require(x2y1 / x2 == y1, "overflow x2y1 detected");

    uint256 x1y2 = x1 * y2;
    if (x1 != 0) require(x1y2 / x1 == y2, "overflow x1y2 detected");

    x2 = x2 / mulPrecision();
    y2 = y2 / mulPrecision();
    uint256 x2y2 = x2 * y2;
    if (x2 != 0) require(x2y2 / x2 == y2, "overflow x2y2 detected");

    // result = fixed1() * x1 * y1 + x1 * y2 + x2 * y1 + x2 * y2 / fixed1();
    Fraction memory result = Fraction(x1y1);
    result = add(result, Fraction(x2y1)); // Add checks for overflow
    result = add(result, Fraction(x1y2)); // Add checks for overflow
    result = add(result, Fraction(x2y2)); // Add checks for overflow
    return result;
  }

  /**
   * @notice 1/x
   * @dev
   * Test reciprocal(0) fails
   * Test reciprocal(fixed1()) returns fixed1()
   * Test reciprocal(fixed1()*fixed1()) returns 1 // Testing how the fractional is truncated
   * Test reciprocal(1+fixed1()*fixed1()) returns 0 // Testing how the fractional is truncated
   * Test reciprocal(newFixedFraction(1, 1e24)) returns newFixed(1e24)
   */
  function reciprocal(Fraction memory x) internal pure returns (Fraction memory) {
    require(x.value != 0, "can't call reciprocal(0)");
    return Fraction((FIXED1_UINT * FIXED1_UINT) / x.value); // Can't overflow
  }

  /**
   * @notice x/y. If the dividend is higher than the max dividend value, it
   * might overflow. You can use multiply(x,reciprocal(y)) instead.
   * @dev The maximum value that can be safely used as a dividend (maxNewFixed) is defined as
   * divide(maxNewFixed,newFixedFraction(1,fixed1())) is around maxUint256().
   * This yields the value 115792089237316195423570985008687907853269984665640564.
   * Test maxNewFixed equals maxUint256()/fixed1()
   * Test divide(maxNewFixed,1) equals maxNewFixed*(fixed1)
   * Test divide(maxNewFixed+1,multiply(mulPrecision(),mulPrecision())) throws
   * Test divide(fixed1(),0) fails
   * Test divide(maxNewFixed,1) = maxNewFixed*(10^digits())
   * Test divide(maxNewFixed+1,1) throws
   */
  function divide(Fraction memory x, Fraction memory y) internal pure returns (Fraction memory) {
    require(y.value != 0, "can't divide by 0");
    uint256 X = x.value * FIXED1_UINT;
    require(X / FIXED1_UINT == x.value, "overflow at divide");
    return Fraction(X / y.value);
  }

  /**
   * @notice x > y
   */
  function gt(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value > y.value;
  }

  /**
   * @notice x >= y
   */
  function gte(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value >= y.value;
  }

  /**
   * @notice x < y
   */
  function lt(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value < y.value;
  }

  /**
   * @notice x <= y
   */
  function lte(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value <= y.value;
  }

  /**
   * @notice x == y
   */
  function equals(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value == y.value;
  }

  /**
   * @notice x <= 1
   */
  function isProperFraction(Fraction memory x) internal pure returns (bool) {
    return lte(x, fixed1());
  }
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.7 <0.8.20;

interface IScoreReader {
  function getValidatorScore(address validator) external view returns (uint256);
  function getGroupScore(address validator) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.13 <0.9.0;

/// Possibly not final version
interface IOracle {
  function getExchangeRate(
    address token
  ) external view returns (uint256 numerator, uint256 denominator);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IEpochManagerInitializer {
  function initialize(address registryAddress, uint256 newEpochDuration) external;
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.0 <0.8.20;

// Note: This is not an exact copy of UsingRegistry or UsingRegistryV2 in the contract's folder
// because Mento's interfaces still don't support Solidity 0.8

import "@openzeppelin/contracts8/access/Ownable.sol";
import "@openzeppelin/contracts8/token/ERC20/IERC20.sol";

import "../../contracts/common/interfaces/IRegistry.sol";
import "../../contracts/common/interfaces/IAccounts.sol";
import "../../contracts/common/interfaces/IEpochManager.sol";
import "../../contracts/common/interfaces/IFreezer.sol";
import "../../contracts/common/interfaces/ICeloUnreleasedTreasury.sol";
import "../../contracts/common/interfaces/IFeeCurrencyWhitelist.sol";
import "../../contracts/common/interfaces/IFeeHandlerSeller.sol";
import "../../contracts/common/interfaces/IEpochManager.sol";
import "../../contracts/governance/interfaces/IGovernance.sol";
import "../../contracts/governance/interfaces/ILockedGold.sol";
import "../../contracts/governance/interfaces/ILockedCelo.sol";
import "../../contracts/governance/interfaces/IValidators.sol";
import "../../contracts/governance/interfaces/IElection.sol";
import "../../contracts/governance/interfaces/IEpochRewards.sol";
import "../../contracts/stability/interfaces/ISortedOracles.sol";

import "./interfaces/IScoreReader.sol";

contract UsingRegistry is Ownable {
  // solhint-disable state-visibility
  bytes32 constant ACCOUNTS_REGISTRY_ID = keccak256(abi.encodePacked("Accounts"));
  bytes32 constant ATTESTATIONS_REGISTRY_ID = keccak256(abi.encodePacked("Attestations"));
  bytes32 constant DOWNTIME_SLASHER_REGISTRY_ID = keccak256(abi.encodePacked("DowntimeSlasher"));
  bytes32 constant DOUBLE_SIGNING_SLASHER_REGISTRY_ID =
    keccak256(abi.encodePacked("DoubleSigningSlasher"));
  bytes32 constant ELECTION_REGISTRY_ID = keccak256(abi.encodePacked("Election"));
  bytes32 constant EPOCH_REWARDS_REGISTRY_ID = keccak256(abi.encodePacked("EpochRewards"));
  bytes32 constant EXCHANGE_REGISTRY_ID = keccak256(abi.encodePacked("Exchange"));
  bytes32 constant FEE_CURRENCY_WHITELIST_REGISTRY_ID =
    keccak256(abi.encodePacked("FeeCurrencyWhitelist"));
  bytes32 constant FREEZER_REGISTRY_ID = keccak256(abi.encodePacked("Freezer"));
  bytes32 constant GOLD_TOKEN_REGISTRY_ID = keccak256(abi.encodePacked("GoldToken"));
  bytes32 constant GOVERNANCE_REGISTRY_ID = keccak256(abi.encodePacked("Governance"));
  bytes32 constant GOVERNANCE_SLASHER_REGISTRY_ID =
    keccak256(abi.encodePacked("GovernanceSlasher"));
  bytes32 constant LOCKED_GOLD_REGISTRY_ID = keccak256(abi.encodePacked("LockedGold"));
  bytes32 constant RESERVE_REGISTRY_ID = keccak256(abi.encodePacked("Reserve"));
  bytes32 constant RANDOM_REGISTRY_ID = keccak256(abi.encodePacked("Random"));
  bytes32 constant SORTED_ORACLES_REGISTRY_ID = keccak256(abi.encodePacked("SortedOracles"));
  bytes32 constant STABLE_TOKEN_REGISTRY_ID = keccak256(abi.encodePacked("StableToken"));
  bytes32 constant VALIDATORS_REGISTRY_ID = keccak256(abi.encodePacked("Validators"));
  bytes32 constant MENTOFEEHANDLERSELLER_REGISTRY_ID =
    keccak256(abi.encodePacked("MentoFeeHandlerSeller"));
  bytes32 constant CELO_TOKEN_REGISTRY_ID = keccak256(abi.encodePacked("CeloToken"));
  bytes32 constant LOCKED_CELO_REGISTRY_ID = keccak256(abi.encodePacked("LockedCelo"));
  bytes32 constant CELO_UNRELEASED_TREASURY_REGISTRY_ID =
    keccak256(abi.encodePacked("CeloUnreleasedTreasury"));
  bytes32 constant EPOCH_MANAGER_ENABLER_REGISTRY_ID =
    keccak256(abi.encodePacked("EpochManagerEnabler"));
  bytes32 constant EPOCH_MANAGER_REGISTRY_ID = keccak256(abi.encodePacked("EpochManager"));
  bytes32 constant SCORE_MANAGER_REGISTRY_ID = keccak256(abi.encodePacked("ScoreManager"));
  // solhint-enable state-visibility

  IRegistry public registry;

  event RegistrySet(address indexed registryAddress);

  modifier onlyRegisteredContract(bytes32 identifierHash) {
    require(registry.getAddressForOrDie(identifierHash) == msg.sender, "only registered contract");
    _;
  }

  modifier onlyRegisteredContracts(bytes32[] memory identifierHashes) {
    require(registry.isOneOf(identifierHashes, msg.sender), "only registered contracts");
    _;
  }

  /**
   * @notice Updates the address pointing to a Registry contract.
   * @param registryAddress The address of a registry contract for routing to other contracts.
   */
  function setRegistry(address registryAddress) public onlyOwner {
    require(registryAddress != address(0), "Cannot register the null address");
    registry = IRegistry(registryAddress);
    emit RegistrySet(registryAddress);
  }

  function getGoldToken() internal view returns (IERC20) {
    return IERC20(registry.getAddressForOrDie(GOLD_TOKEN_REGISTRY_ID));
  }

  function getCeloToken() internal view returns (IERC20) {
    return IERC20(registry.getAddressForOrDie(CELO_TOKEN_REGISTRY_ID));
  }

  function getFreezer() internal view returns (IFreezer) {
    return IFreezer(registry.getAddressForOrDie(FREEZER_REGISTRY_ID));
  }

  function getSortedOracles() internal view returns (ISortedOracles) {
    return ISortedOracles(registry.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID));
  }

  function getFeeCurrencyWhitelist() internal view returns (IFeeCurrencyWhitelist) {
    return IFeeCurrencyWhitelist(registry.getAddressForOrDie(FEE_CURRENCY_WHITELIST_REGISTRY_ID));
  }

  function getLockedGold() internal view returns (ILockedGold) {
    return ILockedGold(registry.getAddressForOrDie(LOCKED_GOLD_REGISTRY_ID));
  }

  function getLockedCelo() internal view returns (ILockedCelo) {
    return ILockedCelo(registry.getAddressForOrDie(LOCKED_CELO_REGISTRY_ID));
  }

  // Current version of Mento doesn't support 0.8
  function getStableToken() internal view returns (address) {
    return registry.getAddressForOrDie(STABLE_TOKEN_REGISTRY_ID);
  }

  function getMentoFeeHandlerSeller() internal view returns (IFeeHandlerSeller) {
    return IFeeHandlerSeller(registry.getAddressForOrDie(MENTOFEEHANDLERSELLER_REGISTRY_ID));
  }

  function getAccounts() internal view returns (IAccounts) {
    return IAccounts(registry.getAddressForOrDie(ACCOUNTS_REGISTRY_ID));
  }

  function getValidators() internal view returns (IValidators) {
    return IValidators(registry.getAddressForOrDie(VALIDATORS_REGISTRY_ID));
  }

  function getElection() internal view returns (IElection) {
    return IElection(registry.getAddressForOrDie(ELECTION_REGISTRY_ID));
  }

  function getEpochRewards() internal view returns (IEpochRewards) {
    return IEpochRewards(registry.getAddressForOrDie(EPOCH_REWARDS_REGISTRY_ID));
  }

  function getGovernance() internal view returns (IGovernance) {
    return IGovernance(registry.getAddressForOrDie(GOVERNANCE_REGISTRY_ID));
  }

  function getCeloUnreleasedTreasury() internal view returns (ICeloUnreleasedTreasury) {
    return
      ICeloUnreleasedTreasury(registry.getAddressForOrDie(CELO_UNRELEASED_TREASURY_REGISTRY_ID));
  }

  function getEpochManager() internal view returns (IEpochManager) {
    return IEpochManager(registry.getAddressForOrDie(EPOCH_MANAGER_REGISTRY_ID));
  }

  function getScoreReader() internal view returns (IScoreReader) {
    return IScoreReader(registry.getAddressForOrDie(SCORE_MANAGER_REGISTRY_ID));
  }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

Settings
{
  "remappings": [],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "paris",
  "libraries": {},
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"bool","name":"test","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"newEpochDuration","type":"uint256"}],"name":"EpochDurationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epochNumber","type":"uint256"}],"name":"EpochProcessingEnded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epochNumber","type":"uint256"}],"name":"EpochProcessingStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"group","type":"address"},{"indexed":true,"internalType":"uint256","name":"epochNumber","type":"uint256"}],"name":"GroupMarkedForProcessing","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"group","type":"address"},{"indexed":true,"internalType":"uint256","name":"epochNumber","type":"uint256"}],"name":"GroupProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOracleAddress","type":"address"}],"name":"OracleAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"registryAddress","type":"address"}],"name":"RegistrySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"validator","type":"address"},{"indexed":false,"internalType":"uint256","name":"validatorPayment","type":"uint256"},{"indexed":true,"internalType":"address","name":"group","type":"address"},{"indexed":false,"internalType":"uint256","name":"groupPayment","type":"uint256"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"delegatedPayment","type":"uint256"}],"name":"ValidatorEpochPaymentDistributed","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"electedAccounts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"electedSigners","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochProcessing","outputs":[{"internalType":"enum EpochManager.EpochProcessStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"perValidatorReward","type":"uint256"},{"internalType":"uint256","name":"totalRewardsVoter","type":"uint256"},{"internalType":"uint256","name":"totalRewardsCommunity","type":"uint256"},{"internalType":"uint256","name":"totalRewardsCarbonFund","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"groups","type":"address[]"},{"internalType":"address[]","name":"lessers","type":"address[]"},{"internalType":"address[]","name":"greaters","type":"address[]"}],"name":"finishNextEpochProcess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"firstKnownEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","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":"getCurrentEpochNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getElectedAccountByIndex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getElectedAccounts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getElectedSignerByIndex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getElectedSigners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_blockNumber","type":"uint256"}],"name":"getEpochByBlockNumber","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":[{"internalType":"uint256","name":"epochNumber","type":"uint256"}],"name":"getEpochByNumber","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":[{"internalType":"uint256","name":"_blockNumber","type":"uint256"}],"name":"getEpochNumberOfBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEpochProcessingState","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"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":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getFirstBlockAtEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getLastBlockAtEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersionNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"registryAddress","type":"address"},{"internalType":"uint256","name":"newEpochDuration","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"firstEpochNumber","type":"uint256"},{"internalType":"uint256","name":"firstEpochBlock","type":"uint256"},{"internalType":"address[]","name":"firstElected","type":"address[]"}],"name":"initializeSystem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isBlocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isEpochProcessingStarted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isIndividualProcessing","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOnEpochProcess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isSystemInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isTimeForNextEpoch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfElectedInCurrentSet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"group","type":"address"},{"internalType":"address","name":"lesser","type":"address"},{"internalType":"address","name":"greater","type":"address"}],"name":"processGroup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"groups","type":"address[]"},{"internalType":"address[]","name":"lessers","type":"address[]"},{"internalType":"address[]","name":"greaters","type":"address[]"}],"name":"processGroups","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"processedGroups","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract IRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"validator","type":"address"}],"name":"sendValidatorPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newEpochDuration","type":"uint256"}],"name":"setEpochDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOracleAddress","type":"address"}],"name":"setOracleAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"registryAddress","type":"address"}],"name":"setRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setToProcessGroups","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startNextEpochProcess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"systemAlreadyInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toProcessGroups","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"validatorPendingPayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

608060405260006012553480156200001657600080fd5b50604051620045fa380380620045fa8339810160408190526200003991620000bf565b80806200004e576000805460ff191660011790555b506200005a3362000066565b506001600255620000ea565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b600060208284031215620000d257600080fd5b81518015158114620000e357600080fd5b9392505050565b61450080620000fa6000396000f3fe608060405234801561001057600080fd5b506004361061028a5760003560e01c80638657f6151161015c578063b5a842cd116100ce578063d75f0da711610087578063d75f0da71461056e578063ebf52c5114610576578063ef0551d1146105a6578063f1542de5146105b9578063f1fcbab1146105cc578063f2fde38b146105d557600080fd5b8063b5a842cd14610528578063b97dd9e21461053b578063ba18ee4d14610543578063bd5a0a351461054b578063cd6dc68714610553578063d02cfcf31461056657600080fd5b806398f6c7601161012057806398f6c760146104ca5780639b217558146104ea5780639f3bf7a0146104f2578063a0fa0d56146104fa578063a89ae4ba14610502578063a91ee0dc1461051557600080fd5b80638657f61514610457578063891c48fb1461046a5780638c25656e146104735780638da5cb5b1461048657806398b509051461049c57600080fd5b806351ccd8f01161020057806364c2c255116101b957806364c2c255146103f4578063653c356f14610401578063667f331b146104145780636857f6a114610427578063715018a61461043c5780637b1039991461044457600080fd5b806351ccd8f01461038457806352ba62621461039757806354255be0146103aa57806354454b48146103d15780635915e077146103d9578063639a84d3146103ec57600080fd5b80633a65c499116102525780633a65c499146103325780633b1eb4bf146103455780634406d661146103585780634408d2ba146103605780634c69c00f146103685780634ff0876a1461037b57600080fd5b8063158ef93e1461028f578063186b978d146102b1578063210b315b146102df5780632ba47274146102f457806330024dfe1461031f575b600080fd5b60005461029c9060ff1681565b60405190151581526020015b60405180910390f35b6102d16102bf366004613d6e565b60096020526000908152604090205481565b6040519081526020016102a8565b6102f26102ed366004613d92565b6105e8565b005b610307610302366004613ddd565b6107e4565b6040516001600160a01b0390911681526020016102a8565b6102f261032d366004613ddd565b610838565b610307610340366004613ddd565b610946565b6102d1610353366004613ddd565b610970565b61029c6109ad565b6102d16109ca565b6102f2610376366004613d6e565b6109f7565b6102d160045481565b6102f2610392366004613e61565b610b8a565b6102f26103a5366004613f61565b611066565b6001806000805b6040805194855260208501939093529183015260608201526080016102a8565b61029c611104565b6102f26103e7366004613d6e565b611126565b6102d1611663565b60035461029c9060ff1681565b6103b161040f366004613ddd565b611690565b6102f2610422366004613f61565b6116dd565b61042f611d00565b6040516102a89190613ffb565b6102f2611d86565b600154610307906001600160a01b031681565b610307610465366004613ddd565b611d9a565b6102d160055481565b610307610481366004613ddd565b611dd3565b60005461010090046001600160a01b0316610307565b600a54600b54600c54600d54600e546104b99460ff169392919085565b6040516102a895949392919061405e565b6102d16104d8366004613d6e565b60106020526000908152604090205481565b61029c611de3565b6102f2611dfb565b61029c612084565b600754610307906001600160a01b031681565b6102f2610523366004613d6e565b6120af565b6102d1610536366004613ddd565b612157565b6103b16121fd565b61029c612240565b61042f612249565b6102f26105613660046140a1565b6122cd565b6102f26123f4565b61029c6126df565b61057e6126e9565b604080519586526020860194909452928401919091526060830152608082015260a0016102a8565b6102d16105b4366004613ddd565b612732565b6103b16105c7366004613ddd565b6127d5565b6102d160125481565b6102f26105e3366004613d6e565b61284d565b600a6105f2611104565b6106535760405162461bcd60e51b815260206004820152602760248201527f496e646976756475616c2065706f63682070726f63657373206973206e6f74206044820152661cdd185c9d195960ca1b60648201526084015b60405180910390fd5b6000601254116106a55760405162461bcd60e51b815260206004820152601960248201527f6e6f206d6f72652067726f75707320746f2070726f6365737300000000000000604482015260640161064a565b6001600160a01b038416600090815260096020526040902054806106db5760405162461bcd60e51b815260040161064a906140cd565b60006106e56128c6565b90506000198214610763576040516312541a6b60e01b81526001600160a01b03878116600483015260248201849052868116604483015285811660648301528216906312541a6b90608401600060405180830381600087803b15801561074a57600080fd5b505af115801561075e573d6000803e3d6000fd5b505050505b6001600160a01b0386166000908152600960205260408120819055601280549161078c83614125565b90915550506006546040516001600160a01b038816907f15ff42ed3ffc7d45600460c399f3a885b3c25e0c4f613266e5fbd5dc80ea14c790600090a36012546000036107dc576107dc8382612969565b505050505050565b60006107ee611de3565b61080a5760405162461bcd60e51b815260040161064a9061413c565b6011828154811061081d5761081d614173565b6000918252602090912001546001600160a01b031692915050565b610840612cb1565b600081116108a65760405162461bcd60e51b815260206004820152602d60248201527f4e65772065706f6368206475726174696f6e206d75737420626520677265617460448201526c32b9103a3430b7103d32b9379760991b606482015260840161064a565b6108ae612240565b156109135760405162461bcd60e51b815260206004820152602f60248201527f43616e6e6f74206368616e67652065706f6368206475726174696f6e2064757260448201526e34b73390383937b1b2b9b9b4b7339760891b606482015260840161064a565b600481905560405181907fe018a3a09e27f3987d8cbe7e32e55899996a50099fb79f333c0030831842eeb090600090a250565b6011818154811061095657600080fd5b6000918252602090912001546001600160a01b0316905081565b600061097a611de3565b6109965760405162461bcd60e51b815260040161064a9061413c565b60006109a183612d11565b50929695505050505050565b60006109b7612240565b806109c557506109c5611104565b905090565b60006109d4611de3565b6109f05760405162461bcd60e51b815260040161064a9061413c565b5060065490565b6109ff612cb1565b6001600160a01b038116610a645760405162461bcd60e51b815260206004820152602660248201527f43616e6e6f74207365742061646472657373207a65726f20617320746865204f6044820152653930b1b6329760d11b606482015260840161064a565b6007546001600160a01b0390811690821603610acd5760405162461bcd60e51b815260206004820152602260248201527f4f7261636c6520616464726573732063616e6e6f74206265207468652073616d604482015261329760f11b606482015260840161064a565b610ad5612240565b15610b405760405162461bcd60e51b815260206004820152603560248201527f43616e6e6f74206368616e6765206f7261636c6520616464726573732064757260448201527434b7339032b837b1b410383937b1b2b9b9b4b7339760591b606482015260840161064a565b600780546001600160a01b0319166001600160a01b0383169081179091556040517fdd82bdfc28fcc826c53263dc2a2b6083e27e92699ebfee617227e08e21c202f090600090a250565b6001546040517222b837b1b426b0b730b3b2b922b730b13632b960691b60208201526001600160a01b039091169063dcf0aaed90603301604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401610bf591815260200190565b602060405180830381865afa158015610c12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c369190614189565b6001600160a01b0316336001600160a01b031614610c965760405162461bcd60e51b815260206004820152601960248201527f6d73672e73656e646572206973206e6f7420456e61626c657200000000000000604482015260640161064a565b6000610ca0612fa9565b6001546040517543656c6f556e72656c6561736564547265617375727960501b60208201526001600160a01b03928316926370a0823192169063dcf0aaed90603601604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401610d1691815260200190565b602060405180830381865afa158015610d33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d579190614189565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610d9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dbf91906141a6565b11610e1b5760405162461bcd60e51b815260206004820152602660248201527f43656c6f556e72656c65617365645472656173757279206e6f742079657420666044820152653ab73232b21760d11b606482015260840161064a565b610e23611de3565b15610e705760405162461bcd60e51b815260206004820181905260248201527f45706f63682073797374656d20616c726561647920696e697469616c697a6564604482015260640161064a565b60008311610ed25760405162461bcd60e51b815260206004820152602960248201527f46697273742065706f6368206e756d626572206d75737420626520677265617460448201526806572207468616e20360bc1b606482015260840161064a565b60008211610f335760405162461bcd60e51b815260206004820152602860248201527f46697273742065706f636820626c6f636b206d75737420626520677265617465604482015267072207468616e20360c41b606482015260840161064a565b43821115610fa95760405162461bcd60e51b815260206004820152603a60248201527f46697273742065706f636820626c6f636b206d757374206265206c657373206f60448201527f7220657175616c207468616e2063757272656e7420626c6f636b000000000000606482015260840161064a565b60008151116110125760405162461bcd60e51b815260206004820152602f60248201527f466972737420656c65637465642076616c696461746f7273206d75737420626560448201526e02067726561746572207468616e203608c1b606482015260840161064a565b6003805460ff19166001179055600583905560068390556000838152600f602090815260409091208381554260028201558251909161105691600891850190613cdf565b5061106082612fdc565b50505050565b60005b858110156110fb576110e987878381811061108657611086614173565b905060200201602081019061109b9190613d6e565b8686848181106110ad576110ad614173565b90506020020160208101906110c29190613d6e565b8585858181106110d4576110d4614173565b90506020020160208101906102ed9190613d6e565b806110f3816141bf565b915050611069565b50505050505050565b600060025b600a5460ff16600281111561112057611120614048565b14905090565b61112e611de3565b61114a5760405162461bcd60e51b815260040161064a9061413c565b6001600160a01b03811660009081526010602052604081205461116c90613190565b6001600160a01b038316600090815260106020526040812081905590915061119261324e565b60405163bb0f1fa560e01b81526001600160a01b03858116600483015291925060009183169063bb0f1fa590602401602060405180830381865afa1580156111de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112029190614189565b604051639b9d516160e01b81526001600160a01b038083166004830152919250600091841690639b9d516190602401600060405180830381865afa15801561124e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112769190810190614247565b505050505091505060006112b46112af6112a88460408051602080820183526000909152815190810190915290815290565b8790613282565b6135f3565b905060006112d4826112c5886135f3565b6112cf9190614332565b613190565b90506000806112e161360d565b604051639f024f4b60e01b81526001600160a01b038b811660048301529190911690639f024f4b906024016040805180830381865afa158015611328573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134c9190614345565b9150915060006113816112af61137a8460408051602080820183526000909152815190810190915290815290565b8690613282565b905060008161138f866135f3565b6113999190614332565b905060006113a561363f565b9050811561146c5760405163a9059cbb60e01b81526001600160a01b038d811660048301526024820184905282169063a9059cbb906044016020604051808303816000875af11580156113fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114209190614373565b61146c5760405162461bcd60e51b815260206004820152601c60248201527f7472616e73666572206661696c656420746f2076616c696461746f7200000000604482015260640161064a565b861561153c5760405163a9059cbb60e01b81526001600160a01b038a811660048301526024820189905282169063a9059cbb906044016020604051808303816000875af11580156114c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e59190614373565b61153c5760405162461bcd60e51b815260206004820152602260248201527f7472616e73666572206661696c656420746f2076616c696461746f722067726f604482015261075760f41b606482015260840161064a565b82156116015760405163a9059cbb60e01b81526001600160a01b0386811660048301526024820185905282169063a9059cbb906044016020604051808303816000875af1158015611591573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b59190614373565b6116015760405162461bcd60e51b815260206004820152601c60248201527f7472616e73666572206661696c656420746f2064656c65676174656500000000604482015260640161064a565b60408051838152602081018990529081018490526001600160a01b03808716918b8216918f16907fee2788e7abedfc61d9608e143b172de1a608a4298b06ed8c84838aa0ad6bd1369060600160405180910390a4505050505050505050505050565b600061166d611de3565b6116895760405162461bcd60e51b815260040161064a9061413c565b5060085490565b60008060008061169e611de3565b6116ba5760405162461bcd60e51b815260040161064a9061413c565b6000806000806116c989612d11565b929d919c509a509098509650505050505050565b6116e5613674565b6116ed612240565b6117395760405162461bcd60e51b815260206004820152601c60248201527f45706f63682070726f63657373206973206e6f74207374617274656400000000604482015260640161064a565b601254156117af5760405162461bcd60e51b815260206004820152603e60248201527f43616e27742066696e6973682065706f6368207768696c6520696e646976696460448201527f75616c2067726f75707320617265206265696e672070726f6365737365640000606482015260840161064a565b600a6000806117bc61324e565b905060006117c86128c6565b905060006117d46136cb565b601154600854919250146117fa5760405162461bcd60e51b815260040161064a90614395565b60005b600854811015611a5a576000846001600160a01b031663bb0f1fa56008848154811061182b5761182b614173565b60009182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602401602060405180830381865afa15801561187b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189f9190614189565b6001600160a01b038116600090815260096020526040812054919250036119ee57856118ca816141bf565b6040516310e0415560e11b81526001600160a01b03848116600483015291985060009250908516906321c082aa90602401602060405180830381865afa158015611918573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193c91906141a6565b6002890154604051622400b960e71b81526001600160a01b038581166004830152602482019290925260448101839052919250600091908716906312005c8090606401602060405180830381865afa15801561199c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119c091906141a6565b905080156119ce57806119d2565b6000195b6001600160a01b03841660009081526009602052604090205550505b60088281548110611a0157611a01614173565b600091825260209091200180546001600160a01b03191690556011805483908110611a2e57611a2e614173565b600091825260209091200180546001600160a01b03191690555080611a52816141bf565b9150506117fd565b50838a14611aaa5760405162461bcd60e51b815260206004820152601f60248201527f6e756d626572206f662067726f75707320646f6573206e6f74206d6174636800604482015260640161064a565b60005b8a811015611ce6576000600960008e8e85818110611acd57611acd614173565b9050602002016020810190611ae29190613d6e565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905060008111611b255760405162461bcd60e51b815260040161064a906140cd565b6000198114611c2557836001600160a01b03166312541a6b8e8e85818110611b4f57611b4f614173565b9050602002016020810190611b649190613d6e565b838e8e87818110611b7757611b77614173565b9050602002016020810190611b8c9190613d6e565b8d8d88818110611b9e57611b9e614173565b9050602002016020810190611bb39190613d6e565b60405160e086901b6001600160e01b03191681526001600160a01b039485166004820152602481019390935290831660448301529091166064820152608401600060405180830381600087803b158015611c0c57600080fd5b505af1158015611c20573d6000803e3d6000fd5b505050505b600960008e8e85818110611c3b57611c3b614173565b9050602002016020810190611c509190613d6e565b6001600160a01b03166001600160a01b03168152602001908152602001600020600090556006548d8d84818110611c8957611c89614173565b9050602002016020810190611c9e9190613d6e565b6001600160a01b03167f15ff42ed3ffc7d45600460c399f3a885b3c25e0c4f613266e5fbd5dc80ea14c760405160405180910390a35080611cde816141bf565b915050611aad565b50611cf18583612969565b50505050506107dc6001600255565b6060611d0a611de3565b611d265760405162461bcd60e51b815260040161064a9061413c565b6008805480602002602001604051908101604052809291908181526020018280548015611d7c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611d5e575b5050505050905090565b611d8e612cb1565b611d986000613701565b565b6000611da4611de3565b611dc05760405162461bcd60e51b815260040161064a9061413c565b6008828154811061081d5761081d614173565b6008818154811061095657600080fd5b6000805460ff1680156109c557505060035460ff1690565b611e03613674565b611e0b611de3565b611e275760405162461bcd60e51b815260040161064a9061413c565b611e2f612084565b611e7b5760405162461bcd60e51b815260206004820152601b60248201527f45706f6368206973206e6f7420726561647920746f2073746172740000000000604482015260640161064a565b6000600a5460ff166002811115611e9457611e94614048565b14611ee15760405162461bcd60e51b815260206004820181905260248201527f45706f63682070726f6365737320697320616c72656164792073746172746564604482015260640161064a565b611ee96109ad565b15611f365760405162461bcd60e51b815260206004820181905260248201527f45706f63682070726f6365737320697320616c72656164792073746172746564604482015260640161064a565b600a805460ff191660011790556006546000908152600f6020526040902043600390910155611f6361375a565b6001600160a01b03166392ecd7456040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611f9d57600080fd5b505af1158015611fb1573d6000803e3d6000fd5b50505050600080600080611fc361375a565b6001600160a01b031663643470436040518163ffffffff1660e01b8152600401608060405180830381865afa158015612000573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202491906143e7565b600b849055600c839055600d829055600e8190559296509094509250905061204a613790565b6006546040517fae58a33f8b8d696bcbaca9fa29d9fdc336c140e982196c2580db3d46f3e6d4b690600090a250505050611d986001600255565b6004546006546000908152600f602052604081206002015490916120a79161441d565b421015905090565b6120b7612cb1565b6001600160a01b03811661210d5760405162461bcd60e51b815260206004820181905260248201527f43616e6e6f7420726567697374657220746865206e756c6c2061646472657373604482015260640161064a565b600180546001600160a01b0319166001600160a01b0383169081179091556040517f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b90600090a250565b600060055482101561219d5760405162461bcd60e51b815260206004820152600f60248201526e22b837b1b4103737ba1035b737bbb760891b604482015260640161064a565b60065482106121e75760405162461bcd60e51b8152602060048201526016602482015275115c1bd8da081b9bdd08199a5b9a5cda1959081e595d60521b604482015260640161064a565b506000908152600f602052604090206001015490565b60008060008061220b611de3565b6122275760405162461bcd60e51b815260040161064a9061413c565b6122326006546127d5565b935093509350935090919293565b60006001611109565b6060612253611de3565b61226f5760405162461bcd60e51b815260040161064a9061413c565b6011805480602002602001604051908101604052809291908181526020018280548015611d7c576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311611d5e575050505050905090565b60005460ff16156123205760405162461bcd60e51b815260206004820152601c60248201527f636f6e747261637420616c726561647920696e697469616c697a656400000000604482015260640161064a565b6000805460ff1916600117905561233633613701565b61233f826120af565b61234881610838565b6001546040516c536f727465644f7261636c657360981b60208201526123f0916001600160a01b03169063dcf0aaed90602d01604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004016123af91815260200190565b602060405180830381865afa1580156123cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103769190614189565b5050565b6123fc612240565b6124485760405162461bcd60e51b815260206004820152601c60248201527f45706f63682070726f63657373206973206e6f74207374617274656400000000604482015260640161064a565b600a805460ff19166002178155600061245f61324e565b9050600061246b6128c6565b905060006124776136cb565b6011546008549192501461249d5760405162461bcd60e51b815260040161064a90614395565b60005b6008548110156126d8576000846001600160a01b031663bb0f1fa5600884815481106124ce576124ce614173565b60009182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602401602060405180830381865afa15801561251e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125429190614189565b6001600160a01b038116600090815260096020526040812054919250036126c55760128054906000612573836141bf565b90915550506040516310e0415560e11b81526001600160a01b038281166004830152600091908516906321c082aa90602401602060405180830381865afa1580156125c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e691906141a6565b6002880154604051622400b960e71b81526001600160a01b038581166004830152602482019290925260448101839052919250600091908716906312005c8090606401602060405180830381865afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061266a91906141a6565b90508015612678578061267c565b6000195b6001600160a01b0384166000818152600960205260408082209390935560065492517f3ec722331926c18d5406855ed1fea4cd09f6bebb788160c5f43b107525d4b3329190a350505b50806126d0816141bf565b9150506124a0565b5050505050565b60006109c56109ad565b600a805460009182918291829182919060ff16600281111561270d5761270d614048565b6001820154600283015460038401546004909401549299919850965091945092509050565b60006005548210156127785760405162461bcd60e51b815260206004820152600f60248201526e22b837b1b4103737ba1035b737bbb760891b604482015260640161064a565b6006548211156127c25760405162461bcd60e51b8152602060048201526015602482015274115c1bd8da081b9bdd0818dc99585d1959081e595d605a1b604482015260640161064a565b506000908152600f602052604090205490565b6000806000806127e3611de3565b6127ff5760405162461bcd60e51b815260040161064a9061413c565b505050600091825250600f6020908152604091829020825160808101845281548082526001830154938201849052600283015494820185905260039092015460609091018190529093919291565b612855612cb1565b6001600160a01b0381166128ba5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161064a565b6128c381613701565b50565b6001546040516722b632b1ba34b7b760c11b60208201526000916001600160a01b03169063dcf0aaed906028015b604051602081830303815290604052805190602001206040518263ffffffff1660e01b815260040161292891815260200190565b602060405180830381865afa158015612945573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c59190614189565b612974600143614332565b600680546000908152600f6020526040812060010192909255805491612999836141bf565b9091555050600680546000908152600f6020526040808220439055915481528181204260029091015581516330d35a8360e11b8152915190916001600160a01b038416916361a6b5069160048082019286929091908290030181865afa158015612a07573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a2f9190810190614430565b8051909150612a45906008906020840190613cdf565b50612a4f81612fdc565b6000612a59613b95565b60015460405169476f7665726e616e636560b01b60208201529192506001600160a01b0380841692630357371d92919091169063dcf0aaed90602a01604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401612ac991815260200190565b602060405180830381865afa158015612ae6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b0a9190614189565b60038701546040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015612b5557600080fd5b505af1158015612b69573d6000803e3d6000fd5b50505050806001600160a01b0316630357371d612b8461375a565b6001600160a01b03166322dae21f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be59190614189565b86600401546040518363ffffffff1660e01b8152600401612c1b9291906001600160a01b03929092168252602082015260400190565b600060405180830381600087803b158015612c3557600080fd5b505af1158015612c49573d6000803e3d6000fd5b5050855460ff1916865550506000600180860182905560028601829055600386018290556004860191909155600654612c829190614332565b6040517fc8e58d8e6979dd5e68bad79d4a4368a1091f6feb2323e612539b1b84e0663a8f90600090a250505050565b6000546001600160a01b03610100909104163314611d985760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161064a565b6000806000806000612d21611de3565b612d3d5760405162461bcd60e51b815260040161064a9061413c565b43861115612d995760405162461bcd60e51b8152602060048201526024808201527f496e76616c696420626c6f636b4e756d6265722e2056616c756520746f6f206860448201526334b3b41760e11b606482015260840161064a565b6000612da66005546127d5565b505050905080871015612e075760405162461bcd60e51b815260206004820152602360248201527f496e76616c696420626c6f636b4e756d6265722e2056616c756520746f6f206c60448201526237bb9760e91b606482015260840161064a565b6006546000908152600f6020526040902054808810612e4c57600080600080612e316006546127d5565b6006549e50929c50909a5098509650612fa095505050505050565b600554600654600090612e6190600190614332565b90505b808211612f3c5760006002612e79838561441d565b612e83919061446d565b6000818152600f60205260409020805460019091015491925090818d10801590612ead5750808d11155b15612f0c5750506000818152600f602090815260409182902082516080810184528154808252600183015493820184905260028301549482018590526003909201546060909101819052939c509a50985096509450612fa09350505050565b818d1015612f2657612f1f600184614332565b9350612f34565b612f3183600161441d565b94505b505050612e64565b60405162461bcd60e51b815260206004820152603360248201527f4e6f206d61746368696e672065706f636820666f756e6420666f72207468652060448201527233b4bb32b710313637b1b590373ab6b132b91760691b606482015260840161064a565b91939590929450565b6001546040516821b2b637aa37b5b2b760b91b60208201526000916001600160a01b03169063dcf0aaed906029016128f4565b6008546130375760405162461bcd60e51b815260206004820152602360248201527f456c6563746564206c697374206c656e6774682063616e6e6f74206265207a6560448201526239379760e91b606482015260840161064a565b600061304161360d565b9050815167ffffffffffffffff81111561305d5761305d613df6565b604051908082528060200260200182016040528015613086578160200160208202803683370190505b50805161309b91601191602090910190613cdf565b5060005b825181101561318b57816001600160a01b0316634ce38b5f8483815181106130c9576130c9614173565b60200260200101516040518263ffffffff1660e01b81526004016130fc91906001600160a01b0391909116815260200190565b602060405180830381865afa158015613119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061313d9190614189565b6011828154811061315057613150614173565b600091825260209091200180546001600160a01b0319166001600160a01b039290921691909117905580613183816141bf565b91505061309f565b505050565b6040805160208101909152600081527601357c299a88ea76a58924d52ce4f26a85af186c2b9e748211156132255760405162461bcd60e51b815260206004820152603660248201527f63616e277420637265617465206669786964697479206e756d626572206c6172604482015275676572207468616e206d61784e65774669786564282960501b606482015260840161064a565b604051806020016040528069d3c21bcecceda100000084613246919061448f565b905292915050565b6001546040516956616c696461746f727360b01b60208201526000916001600160a01b03169063dcf0aaed90602a016128f4565b6040805160208101909152600081528251158061329e57508151155b156132b857506040805160208101909152600081526135ed565b815169d3c21bcecceda0ffffff19016132d25750816135ed565b825169d3c21bcecceda0ffffff19016132ec5750806135ed565b600069d3c21bcecceda100000061330285613bd5565b5161330d919061446d565b9050600061331a85613c14565b519050600069d3c21bcecceda100000061333386613bd5565b5161333e919061446d565b9050600061334b86613c14565b519050600061335a838661448f565b905084156133b3578261336d868361446d565b146133b35760405162461bcd60e51b81526020600482015260166024820152751bdd995c999b1bddc81e0c5e4c4819195d1958dd195960521b604482015260640161064a565b60006133c969d3c21bcecceda10000008361448f565b905081156134335769d3c21bcecceda10000006133e6838361446d565b146134335760405162461bcd60e51b815260206004820152601f60248201527f6f766572666c6f772078317931202a2066697865643120646574656374656400604482015260640161064a565b9050806000613442858761448f565b9050851561349b5784613455878361446d565b1461349b5760405162461bcd60e51b81526020600482015260166024820152751bdd995c999b1bddc81e0c9e4c4819195d1958dd195960521b604482015260640161064a565b60006134a7858961448f565b9050871561350057846134ba898361446d565b146135005760405162461bcd60e51b81526020600482015260166024820152751bdd995c999b1bddc81e0c5e4c8819195d1958dd195960521b604482015260640161064a565b61350f64e8d4a510008861446d565b965061352064e8d4a510008661446d565b9450600061352e868961448f565b905087156135875785613541898361446d565b146135875760405162461bcd60e51b81526020600482015260166024820152751bdd995c999b1bddc81e0c9e4c8819195d1958dd195960521b604482015260640161064a565b60408051602080820183528782528251908101909252848252906135ac908290613c5f565b90506135c681604051806020016040528086815250613c5f565b90506135e081604051806020016040528085815250613c5f565b9a50505050505050505050505b92915050565b80516000906135ed9069d3c21bcecceda10000009061446d565b600154604051674163636f756e747360c01b60208201526000916001600160a01b03169063dcf0aaed906028016128f4565b6001546040516a29ba30b13632aa37b5b2b760a91b60208201526000916001600160a01b03169063dcf0aaed90602b016128f4565b60028054036136c55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161064a565b60028055565b6001546040516b29b1b7b932a6b0b730b3b2b960a11b60208201526000916001600160a01b03169063dcf0aaed90602c016128f4565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b6001546040516b45706f63685265776172647360a01b60208201526000916001600160a01b03169063dcf0aaed90602c016128f4565b60008061379b6136cb565b905060006137a761324e565b9050600a60005b600854811015613970576000846001600160a01b031663e5079ddc600884815481106137dc576137dc614173565b60009182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602401602060405180830381865afa15801561382c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061385091906141a6565b90506000846001600160a01b031663087c344e6008858154811061387657613876614173565b600091825260209091200154600187015460405160e084901b6001600160e01b03191681526001600160a01b039092166004830152602482018690526044820152606401602060405180830381865afa1580156138d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138fb91906141a6565b905080601060006008868154811061391557613915614173565b60009182526020808320909101546001600160a01b031683528201929092526040018120805490919061394990849061441d565b909155506139599050818861441d565b965050508080613968906141bf565b9150506137ae565b508360000361397f5750505050565b604051633a90822b60e21b8152600481018590526001600160a01b0383169063ea4208ac90602401600060405180830381600087803b1580156139c157600080fd5b505af11580156139d5573d6000803e3d6000fd5b5050600754600092508291506001600160a01b031663efb7601d6139f761363f565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016040805180830381865afa158015613a3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5e91906144a6565b9092509050600081613a70888561448f565b613a7a919061446d565b9050613a84613b95565b600154604051665265736572766560c81b60208201526001600160a01b0392831692630357371d92169063dcf0aaed90602701604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401613aeb91815260200190565b602060405180830381865afa158015613b08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b2c9190614189565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101849052604401600060405180830381600087803b158015613b7457600080fd5b505af1158015613b88573d6000803e3d6000fd5b5050505050505050505050565b6001546040517543656c6f556e72656c6561736564547265617375727960501b60208201526000916001600160a01b03169063dcf0aaed906036016128f4565b604080516020810190915260008152604051806020016040528069d3c21bcecceda1000000808560000151613c0a919061446d565b613246919061448f565b604080516020810190915260008152604051806020016040528069d3c21bcecceda1000000808560000151613c49919061446d565b613c53919061448f565b84516132469190614332565b60408051602081019091526000815281518351600091613c7e9161441d565b8451909150811015613cca5760405162461bcd60e51b8152602060048201526015602482015274185919081bdd995c999b1bddc819195d1958dd1959605a1b604482015260640161064a565b60408051602081019091529081529392505050565b828054828255906000526020600020908101928215613d34579160200282015b82811115613d3457825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613cff565b50613d40929150613d44565b5090565b5b80821115613d405760008155600101613d45565b6001600160a01b03811681146128c357600080fd5b600060208284031215613d8057600080fd5b8135613d8b81613d59565b9392505050565b600080600060608486031215613da757600080fd5b8335613db281613d59565b92506020840135613dc281613d59565b91506040840135613dd281613d59565b809150509250925092565b600060208284031215613def57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715613e3557613e35613df6565b604052919050565b600067ffffffffffffffff821115613e5757613e57613df6565b5060051b60200190565b600080600060608486031215613e7657600080fd5b833592506020808501359250604085013567ffffffffffffffff811115613e9c57600080fd5b8501601f81018713613ead57600080fd5b8035613ec0613ebb82613e3d565b613e0c565b81815260059190911b82018301908381019089831115613edf57600080fd5b928401925b82841015613f06578335613ef781613d59565b82529284019290840190613ee4565b80955050505050509250925092565b60008083601f840112613f2757600080fd5b50813567ffffffffffffffff811115613f3f57600080fd5b6020830191508360208260051b8501011115613f5a57600080fd5b9250929050565b60008060008060008060608789031215613f7a57600080fd5b863567ffffffffffffffff80821115613f9257600080fd5b613f9e8a838b01613f15565b90985096506020890135915080821115613fb757600080fd5b613fc38a838b01613f15565b90965094506040890135915080821115613fdc57600080fd5b50613fe989828a01613f15565b979a9699509497509295939492505050565b6020808252825182820181905260009190848201906040850190845b8181101561403c5783516001600160a01b031683529284019291840191600101614017565b50909695505050505050565b634e487b7160e01b600052602160045260246000fd5b60a081016003871061408057634e487b7160e01b600052602160045260246000fd5b95815260208101949094526040840192909252606083015260809091015290565b600080604083850312156140b457600080fd5b82356140bf81613d59565b946020939093013593505050565b60208082526022908201527f67726f7570206e6f742066726f6d2063757272656e7420656c65637465642073604082015261195d60f21b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b6000816141345761413461410f565b506000190190565b6020808252601c908201527f45706f63682073797374656d206e6f7420696e697469616c697a656400000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561419b57600080fd5b8151613d8b81613d59565b6000602082840312156141b857600080fd5b5051919050565b6000600182016141d1576141d161410f565b5060010190565b600082601f8301126141e957600080fd5b815160206141f9613ebb83613e3d565b82815260059290921b8401810191818101908684111561421857600080fd5b8286015b8481101561423c57805161422f81613d59565b835291830191830161421c565b509695505050505050565b600080600080600080600060e0888a03121561426257600080fd5b875167ffffffffffffffff8082111561427a57600080fd5b6142868b838c016141d8565b985060209150818a0151975060408a0151965060608a0151955060808a0151818111156142b257600080fd5b8a019050601f81018b136142c557600080fd5b80516142d3613ebb82613e3d565b81815260059190911b8201830190838101908d8311156142f257600080fd5b928401925b82841015614310578351825292840192908401906142f7565b809750505050505060a0880151915060c0880151905092959891949750929550565b818103818111156135ed576135ed61410f565b6000806040838503121561435857600080fd5b825161436381613d59565b6020939093015192949293505050565b60006020828403121561438557600080fd5b81518015158114613d8b57600080fd5b60208082526032908201527f456c6563746564206163636f756e747320616e64207369676e657273206f66206040820152713234b33332b932b73a103632b733ba34399760711b606082015260800190565b600080600080608085870312156143fd57600080fd5b505082516020840151604085015160609095015191969095509092509050565b808201808211156135ed576135ed61410f565b60006020828403121561444257600080fd5b815167ffffffffffffffff81111561445957600080fd5b614465848285016141d8565b949350505050565b60008261448a57634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176135ed576135ed61410f565b600080604083850312156144b957600080fd5b50508051602090910151909290915056fea264697066735822122069b5db76bab2d7411c2a04303e2c7ca0c9f04797893d2f480f61d2b8e9c5e9bb64736f6c634300081300330000000000000000000000000000000000000000000000000000000000000001

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061028a5760003560e01c80638657f6151161015c578063b5a842cd116100ce578063d75f0da711610087578063d75f0da71461056e578063ebf52c5114610576578063ef0551d1146105a6578063f1542de5146105b9578063f1fcbab1146105cc578063f2fde38b146105d557600080fd5b8063b5a842cd14610528578063b97dd9e21461053b578063ba18ee4d14610543578063bd5a0a351461054b578063cd6dc68714610553578063d02cfcf31461056657600080fd5b806398f6c7601161012057806398f6c760146104ca5780639b217558146104ea5780639f3bf7a0146104f2578063a0fa0d56146104fa578063a89ae4ba14610502578063a91ee0dc1461051557600080fd5b80638657f61514610457578063891c48fb1461046a5780638c25656e146104735780638da5cb5b1461048657806398b509051461049c57600080fd5b806351ccd8f01161020057806364c2c255116101b957806364c2c255146103f4578063653c356f14610401578063667f331b146104145780636857f6a114610427578063715018a61461043c5780637b1039991461044457600080fd5b806351ccd8f01461038457806352ba62621461039757806354255be0146103aa57806354454b48146103d15780635915e077146103d9578063639a84d3146103ec57600080fd5b80633a65c499116102525780633a65c499146103325780633b1eb4bf146103455780634406d661146103585780634408d2ba146103605780634c69c00f146103685780634ff0876a1461037b57600080fd5b8063158ef93e1461028f578063186b978d146102b1578063210b315b146102df5780632ba47274146102f457806330024dfe1461031f575b600080fd5b60005461029c9060ff1681565b60405190151581526020015b60405180910390f35b6102d16102bf366004613d6e565b60096020526000908152604090205481565b6040519081526020016102a8565b6102f26102ed366004613d92565b6105e8565b005b610307610302366004613ddd565b6107e4565b6040516001600160a01b0390911681526020016102a8565b6102f261032d366004613ddd565b610838565b610307610340366004613ddd565b610946565b6102d1610353366004613ddd565b610970565b61029c6109ad565b6102d16109ca565b6102f2610376366004613d6e565b6109f7565b6102d160045481565b6102f2610392366004613e61565b610b8a565b6102f26103a5366004613f61565b611066565b6001806000805b6040805194855260208501939093529183015260608201526080016102a8565b61029c611104565b6102f26103e7366004613d6e565b611126565b6102d1611663565b60035461029c9060ff1681565b6103b161040f366004613ddd565b611690565b6102f2610422366004613f61565b6116dd565b61042f611d00565b6040516102a89190613ffb565b6102f2611d86565b600154610307906001600160a01b031681565b610307610465366004613ddd565b611d9a565b6102d160055481565b610307610481366004613ddd565b611dd3565b60005461010090046001600160a01b0316610307565b600a54600b54600c54600d54600e546104b99460ff169392919085565b6040516102a895949392919061405e565b6102d16104d8366004613d6e565b60106020526000908152604090205481565b61029c611de3565b6102f2611dfb565b61029c612084565b600754610307906001600160a01b031681565b6102f2610523366004613d6e565b6120af565b6102d1610536366004613ddd565b612157565b6103b16121fd565b61029c612240565b61042f612249565b6102f26105613660046140a1565b6122cd565b6102f26123f4565b61029c6126df565b61057e6126e9565b604080519586526020860194909452928401919091526060830152608082015260a0016102a8565b6102d16105b4366004613ddd565b612732565b6103b16105c7366004613ddd565b6127d5565b6102d160125481565b6102f26105e3366004613d6e565b61284d565b600a6105f2611104565b6106535760405162461bcd60e51b815260206004820152602760248201527f496e646976756475616c2065706f63682070726f63657373206973206e6f74206044820152661cdd185c9d195960ca1b60648201526084015b60405180910390fd5b6000601254116106a55760405162461bcd60e51b815260206004820152601960248201527f6e6f206d6f72652067726f75707320746f2070726f6365737300000000000000604482015260640161064a565b6001600160a01b038416600090815260096020526040902054806106db5760405162461bcd60e51b815260040161064a906140cd565b60006106e56128c6565b90506000198214610763576040516312541a6b60e01b81526001600160a01b03878116600483015260248201849052868116604483015285811660648301528216906312541a6b90608401600060405180830381600087803b15801561074a57600080fd5b505af115801561075e573d6000803e3d6000fd5b505050505b6001600160a01b0386166000908152600960205260408120819055601280549161078c83614125565b90915550506006546040516001600160a01b038816907f15ff42ed3ffc7d45600460c399f3a885b3c25e0c4f613266e5fbd5dc80ea14c790600090a36012546000036107dc576107dc8382612969565b505050505050565b60006107ee611de3565b61080a5760405162461bcd60e51b815260040161064a9061413c565b6011828154811061081d5761081d614173565b6000918252602090912001546001600160a01b031692915050565b610840612cb1565b600081116108a65760405162461bcd60e51b815260206004820152602d60248201527f4e65772065706f6368206475726174696f6e206d75737420626520677265617460448201526c32b9103a3430b7103d32b9379760991b606482015260840161064a565b6108ae612240565b156109135760405162461bcd60e51b815260206004820152602f60248201527f43616e6e6f74206368616e67652065706f6368206475726174696f6e2064757260448201526e34b73390383937b1b2b9b9b4b7339760891b606482015260840161064a565b600481905560405181907fe018a3a09e27f3987d8cbe7e32e55899996a50099fb79f333c0030831842eeb090600090a250565b6011818154811061095657600080fd5b6000918252602090912001546001600160a01b0316905081565b600061097a611de3565b6109965760405162461bcd60e51b815260040161064a9061413c565b60006109a183612d11565b50929695505050505050565b60006109b7612240565b806109c557506109c5611104565b905090565b60006109d4611de3565b6109f05760405162461bcd60e51b815260040161064a9061413c565b5060065490565b6109ff612cb1565b6001600160a01b038116610a645760405162461bcd60e51b815260206004820152602660248201527f43616e6e6f74207365742061646472657373207a65726f20617320746865204f6044820152653930b1b6329760d11b606482015260840161064a565b6007546001600160a01b0390811690821603610acd5760405162461bcd60e51b815260206004820152602260248201527f4f7261636c6520616464726573732063616e6e6f74206265207468652073616d604482015261329760f11b606482015260840161064a565b610ad5612240565b15610b405760405162461bcd60e51b815260206004820152603560248201527f43616e6e6f74206368616e6765206f7261636c6520616464726573732064757260448201527434b7339032b837b1b410383937b1b2b9b9b4b7339760591b606482015260840161064a565b600780546001600160a01b0319166001600160a01b0383169081179091556040517fdd82bdfc28fcc826c53263dc2a2b6083e27e92699ebfee617227e08e21c202f090600090a250565b6001546040517222b837b1b426b0b730b3b2b922b730b13632b960691b60208201526001600160a01b039091169063dcf0aaed90603301604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401610bf591815260200190565b602060405180830381865afa158015610c12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c369190614189565b6001600160a01b0316336001600160a01b031614610c965760405162461bcd60e51b815260206004820152601960248201527f6d73672e73656e646572206973206e6f7420456e61626c657200000000000000604482015260640161064a565b6000610ca0612fa9565b6001546040517543656c6f556e72656c6561736564547265617375727960501b60208201526001600160a01b03928316926370a0823192169063dcf0aaed90603601604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401610d1691815260200190565b602060405180830381865afa158015610d33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d579190614189565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610d9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dbf91906141a6565b11610e1b5760405162461bcd60e51b815260206004820152602660248201527f43656c6f556e72656c65617365645472656173757279206e6f742079657420666044820152653ab73232b21760d11b606482015260840161064a565b610e23611de3565b15610e705760405162461bcd60e51b815260206004820181905260248201527f45706f63682073797374656d20616c726561647920696e697469616c697a6564604482015260640161064a565b60008311610ed25760405162461bcd60e51b815260206004820152602960248201527f46697273742065706f6368206e756d626572206d75737420626520677265617460448201526806572207468616e20360bc1b606482015260840161064a565b60008211610f335760405162461bcd60e51b815260206004820152602860248201527f46697273742065706f636820626c6f636b206d75737420626520677265617465604482015267072207468616e20360c41b606482015260840161064a565b43821115610fa95760405162461bcd60e51b815260206004820152603a60248201527f46697273742065706f636820626c6f636b206d757374206265206c657373206f60448201527f7220657175616c207468616e2063757272656e7420626c6f636b000000000000606482015260840161064a565b60008151116110125760405162461bcd60e51b815260206004820152602f60248201527f466972737420656c65637465642076616c696461746f7273206d75737420626560448201526e02067726561746572207468616e203608c1b606482015260840161064a565b6003805460ff19166001179055600583905560068390556000838152600f602090815260409091208381554260028201558251909161105691600891850190613cdf565b5061106082612fdc565b50505050565b60005b858110156110fb576110e987878381811061108657611086614173565b905060200201602081019061109b9190613d6e565b8686848181106110ad576110ad614173565b90506020020160208101906110c29190613d6e565b8585858181106110d4576110d4614173565b90506020020160208101906102ed9190613d6e565b806110f3816141bf565b915050611069565b50505050505050565b600060025b600a5460ff16600281111561112057611120614048565b14905090565b61112e611de3565b61114a5760405162461bcd60e51b815260040161064a9061413c565b6001600160a01b03811660009081526010602052604081205461116c90613190565b6001600160a01b038316600090815260106020526040812081905590915061119261324e565b60405163bb0f1fa560e01b81526001600160a01b03858116600483015291925060009183169063bb0f1fa590602401602060405180830381865afa1580156111de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112029190614189565b604051639b9d516160e01b81526001600160a01b038083166004830152919250600091841690639b9d516190602401600060405180830381865afa15801561124e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112769190810190614247565b505050505091505060006112b46112af6112a88460408051602080820183526000909152815190810190915290815290565b8790613282565b6135f3565b905060006112d4826112c5886135f3565b6112cf9190614332565b613190565b90506000806112e161360d565b604051639f024f4b60e01b81526001600160a01b038b811660048301529190911690639f024f4b906024016040805180830381865afa158015611328573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134c9190614345565b9150915060006113816112af61137a8460408051602080820183526000909152815190810190915290815290565b8690613282565b905060008161138f866135f3565b6113999190614332565b905060006113a561363f565b9050811561146c5760405163a9059cbb60e01b81526001600160a01b038d811660048301526024820184905282169063a9059cbb906044016020604051808303816000875af11580156113fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114209190614373565b61146c5760405162461bcd60e51b815260206004820152601c60248201527f7472616e73666572206661696c656420746f2076616c696461746f7200000000604482015260640161064a565b861561153c5760405163a9059cbb60e01b81526001600160a01b038a811660048301526024820189905282169063a9059cbb906044016020604051808303816000875af11580156114c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e59190614373565b61153c5760405162461bcd60e51b815260206004820152602260248201527f7472616e73666572206661696c656420746f2076616c696461746f722067726f604482015261075760f41b606482015260840161064a565b82156116015760405163a9059cbb60e01b81526001600160a01b0386811660048301526024820185905282169063a9059cbb906044016020604051808303816000875af1158015611591573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b59190614373565b6116015760405162461bcd60e51b815260206004820152601c60248201527f7472616e73666572206661696c656420746f2064656c65676174656500000000604482015260640161064a565b60408051838152602081018990529081018490526001600160a01b03808716918b8216918f16907fee2788e7abedfc61d9608e143b172de1a608a4298b06ed8c84838aa0ad6bd1369060600160405180910390a4505050505050505050505050565b600061166d611de3565b6116895760405162461bcd60e51b815260040161064a9061413c565b5060085490565b60008060008061169e611de3565b6116ba5760405162461bcd60e51b815260040161064a9061413c565b6000806000806116c989612d11565b929d919c509a509098509650505050505050565b6116e5613674565b6116ed612240565b6117395760405162461bcd60e51b815260206004820152601c60248201527f45706f63682070726f63657373206973206e6f74207374617274656400000000604482015260640161064a565b601254156117af5760405162461bcd60e51b815260206004820152603e60248201527f43616e27742066696e6973682065706f6368207768696c6520696e646976696460448201527f75616c2067726f75707320617265206265696e672070726f6365737365640000606482015260840161064a565b600a6000806117bc61324e565b905060006117c86128c6565b905060006117d46136cb565b601154600854919250146117fa5760405162461bcd60e51b815260040161064a90614395565b60005b600854811015611a5a576000846001600160a01b031663bb0f1fa56008848154811061182b5761182b614173565b60009182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602401602060405180830381865afa15801561187b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189f9190614189565b6001600160a01b038116600090815260096020526040812054919250036119ee57856118ca816141bf565b6040516310e0415560e11b81526001600160a01b03848116600483015291985060009250908516906321c082aa90602401602060405180830381865afa158015611918573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193c91906141a6565b6002890154604051622400b960e71b81526001600160a01b038581166004830152602482019290925260448101839052919250600091908716906312005c8090606401602060405180830381865afa15801561199c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119c091906141a6565b905080156119ce57806119d2565b6000195b6001600160a01b03841660009081526009602052604090205550505b60088281548110611a0157611a01614173565b600091825260209091200180546001600160a01b03191690556011805483908110611a2e57611a2e614173565b600091825260209091200180546001600160a01b03191690555080611a52816141bf565b9150506117fd565b50838a14611aaa5760405162461bcd60e51b815260206004820152601f60248201527f6e756d626572206f662067726f75707320646f6573206e6f74206d6174636800604482015260640161064a565b60005b8a811015611ce6576000600960008e8e85818110611acd57611acd614173565b9050602002016020810190611ae29190613d6e565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905060008111611b255760405162461bcd60e51b815260040161064a906140cd565b6000198114611c2557836001600160a01b03166312541a6b8e8e85818110611b4f57611b4f614173565b9050602002016020810190611b649190613d6e565b838e8e87818110611b7757611b77614173565b9050602002016020810190611b8c9190613d6e565b8d8d88818110611b9e57611b9e614173565b9050602002016020810190611bb39190613d6e565b60405160e086901b6001600160e01b03191681526001600160a01b039485166004820152602481019390935290831660448301529091166064820152608401600060405180830381600087803b158015611c0c57600080fd5b505af1158015611c20573d6000803e3d6000fd5b505050505b600960008e8e85818110611c3b57611c3b614173565b9050602002016020810190611c509190613d6e565b6001600160a01b03166001600160a01b03168152602001908152602001600020600090556006548d8d84818110611c8957611c89614173565b9050602002016020810190611c9e9190613d6e565b6001600160a01b03167f15ff42ed3ffc7d45600460c399f3a885b3c25e0c4f613266e5fbd5dc80ea14c760405160405180910390a35080611cde816141bf565b915050611aad565b50611cf18583612969565b50505050506107dc6001600255565b6060611d0a611de3565b611d265760405162461bcd60e51b815260040161064a9061413c565b6008805480602002602001604051908101604052809291908181526020018280548015611d7c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611d5e575b5050505050905090565b611d8e612cb1565b611d986000613701565b565b6000611da4611de3565b611dc05760405162461bcd60e51b815260040161064a9061413c565b6008828154811061081d5761081d614173565b6008818154811061095657600080fd5b6000805460ff1680156109c557505060035460ff1690565b611e03613674565b611e0b611de3565b611e275760405162461bcd60e51b815260040161064a9061413c565b611e2f612084565b611e7b5760405162461bcd60e51b815260206004820152601b60248201527f45706f6368206973206e6f7420726561647920746f2073746172740000000000604482015260640161064a565b6000600a5460ff166002811115611e9457611e94614048565b14611ee15760405162461bcd60e51b815260206004820181905260248201527f45706f63682070726f6365737320697320616c72656164792073746172746564604482015260640161064a565b611ee96109ad565b15611f365760405162461bcd60e51b815260206004820181905260248201527f45706f63682070726f6365737320697320616c72656164792073746172746564604482015260640161064a565b600a805460ff191660011790556006546000908152600f6020526040902043600390910155611f6361375a565b6001600160a01b03166392ecd7456040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611f9d57600080fd5b505af1158015611fb1573d6000803e3d6000fd5b50505050600080600080611fc361375a565b6001600160a01b031663643470436040518163ffffffff1660e01b8152600401608060405180830381865afa158015612000573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202491906143e7565b600b849055600c839055600d829055600e8190559296509094509250905061204a613790565b6006546040517fae58a33f8b8d696bcbaca9fa29d9fdc336c140e982196c2580db3d46f3e6d4b690600090a250505050611d986001600255565b6004546006546000908152600f602052604081206002015490916120a79161441d565b421015905090565b6120b7612cb1565b6001600160a01b03811661210d5760405162461bcd60e51b815260206004820181905260248201527f43616e6e6f7420726567697374657220746865206e756c6c2061646472657373604482015260640161064a565b600180546001600160a01b0319166001600160a01b0383169081179091556040517f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b90600090a250565b600060055482101561219d5760405162461bcd60e51b815260206004820152600f60248201526e22b837b1b4103737ba1035b737bbb760891b604482015260640161064a565b60065482106121e75760405162461bcd60e51b8152602060048201526016602482015275115c1bd8da081b9bdd08199a5b9a5cda1959081e595d60521b604482015260640161064a565b506000908152600f602052604090206001015490565b60008060008061220b611de3565b6122275760405162461bcd60e51b815260040161064a9061413c565b6122326006546127d5565b935093509350935090919293565b60006001611109565b6060612253611de3565b61226f5760405162461bcd60e51b815260040161064a9061413c565b6011805480602002602001604051908101604052809291908181526020018280548015611d7c576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311611d5e575050505050905090565b60005460ff16156123205760405162461bcd60e51b815260206004820152601c60248201527f636f6e747261637420616c726561647920696e697469616c697a656400000000604482015260640161064a565b6000805460ff1916600117905561233633613701565b61233f826120af565b61234881610838565b6001546040516c536f727465644f7261636c657360981b60208201526123f0916001600160a01b03169063dcf0aaed90602d01604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004016123af91815260200190565b602060405180830381865afa1580156123cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103769190614189565b5050565b6123fc612240565b6124485760405162461bcd60e51b815260206004820152601c60248201527f45706f63682070726f63657373206973206e6f74207374617274656400000000604482015260640161064a565b600a805460ff19166002178155600061245f61324e565b9050600061246b6128c6565b905060006124776136cb565b6011546008549192501461249d5760405162461bcd60e51b815260040161064a90614395565b60005b6008548110156126d8576000846001600160a01b031663bb0f1fa5600884815481106124ce576124ce614173565b60009182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602401602060405180830381865afa15801561251e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125429190614189565b6001600160a01b038116600090815260096020526040812054919250036126c55760128054906000612573836141bf565b90915550506040516310e0415560e11b81526001600160a01b038281166004830152600091908516906321c082aa90602401602060405180830381865afa1580156125c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e691906141a6565b6002880154604051622400b960e71b81526001600160a01b038581166004830152602482019290925260448101839052919250600091908716906312005c8090606401602060405180830381865afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061266a91906141a6565b90508015612678578061267c565b6000195b6001600160a01b0384166000818152600960205260408082209390935560065492517f3ec722331926c18d5406855ed1fea4cd09f6bebb788160c5f43b107525d4b3329190a350505b50806126d0816141bf565b9150506124a0565b5050505050565b60006109c56109ad565b600a805460009182918291829182919060ff16600281111561270d5761270d614048565b6001820154600283015460038401546004909401549299919850965091945092509050565b60006005548210156127785760405162461bcd60e51b815260206004820152600f60248201526e22b837b1b4103737ba1035b737bbb760891b604482015260640161064a565b6006548211156127c25760405162461bcd60e51b8152602060048201526015602482015274115c1bd8da081b9bdd0818dc99585d1959081e595d605a1b604482015260640161064a565b506000908152600f602052604090205490565b6000806000806127e3611de3565b6127ff5760405162461bcd60e51b815260040161064a9061413c565b505050600091825250600f6020908152604091829020825160808101845281548082526001830154938201849052600283015494820185905260039092015460609091018190529093919291565b612855612cb1565b6001600160a01b0381166128ba5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161064a565b6128c381613701565b50565b6001546040516722b632b1ba34b7b760c11b60208201526000916001600160a01b03169063dcf0aaed906028015b604051602081830303815290604052805190602001206040518263ffffffff1660e01b815260040161292891815260200190565b602060405180830381865afa158015612945573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c59190614189565b612974600143614332565b600680546000908152600f6020526040812060010192909255805491612999836141bf565b9091555050600680546000908152600f6020526040808220439055915481528181204260029091015581516330d35a8360e11b8152915190916001600160a01b038416916361a6b5069160048082019286929091908290030181865afa158015612a07573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a2f9190810190614430565b8051909150612a45906008906020840190613cdf565b50612a4f81612fdc565b6000612a59613b95565b60015460405169476f7665726e616e636560b01b60208201529192506001600160a01b0380841692630357371d92919091169063dcf0aaed90602a01604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401612ac991815260200190565b602060405180830381865afa158015612ae6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b0a9190614189565b60038701546040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015612b5557600080fd5b505af1158015612b69573d6000803e3d6000fd5b50505050806001600160a01b0316630357371d612b8461375a565b6001600160a01b03166322dae21f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612be59190614189565b86600401546040518363ffffffff1660e01b8152600401612c1b9291906001600160a01b03929092168252602082015260400190565b600060405180830381600087803b158015612c3557600080fd5b505af1158015612c49573d6000803e3d6000fd5b5050855460ff1916865550506000600180860182905560028601829055600386018290556004860191909155600654612c829190614332565b6040517fc8e58d8e6979dd5e68bad79d4a4368a1091f6feb2323e612539b1b84e0663a8f90600090a250505050565b6000546001600160a01b03610100909104163314611d985760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161064a565b6000806000806000612d21611de3565b612d3d5760405162461bcd60e51b815260040161064a9061413c565b43861115612d995760405162461bcd60e51b8152602060048201526024808201527f496e76616c696420626c6f636b4e756d6265722e2056616c756520746f6f206860448201526334b3b41760e11b606482015260840161064a565b6000612da66005546127d5565b505050905080871015612e075760405162461bcd60e51b815260206004820152602360248201527f496e76616c696420626c6f636b4e756d6265722e2056616c756520746f6f206c60448201526237bb9760e91b606482015260840161064a565b6006546000908152600f6020526040902054808810612e4c57600080600080612e316006546127d5565b6006549e50929c50909a5098509650612fa095505050505050565b600554600654600090612e6190600190614332565b90505b808211612f3c5760006002612e79838561441d565b612e83919061446d565b6000818152600f60205260409020805460019091015491925090818d10801590612ead5750808d11155b15612f0c5750506000818152600f602090815260409182902082516080810184528154808252600183015493820184905260028301549482018590526003909201546060909101819052939c509a50985096509450612fa09350505050565b818d1015612f2657612f1f600184614332565b9350612f34565b612f3183600161441d565b94505b505050612e64565b60405162461bcd60e51b815260206004820152603360248201527f4e6f206d61746368696e672065706f636820666f756e6420666f72207468652060448201527233b4bb32b710313637b1b590373ab6b132b91760691b606482015260840161064a565b91939590929450565b6001546040516821b2b637aa37b5b2b760b91b60208201526000916001600160a01b03169063dcf0aaed906029016128f4565b6008546130375760405162461bcd60e51b815260206004820152602360248201527f456c6563746564206c697374206c656e6774682063616e6e6f74206265207a6560448201526239379760e91b606482015260840161064a565b600061304161360d565b9050815167ffffffffffffffff81111561305d5761305d613df6565b604051908082528060200260200182016040528015613086578160200160208202803683370190505b50805161309b91601191602090910190613cdf565b5060005b825181101561318b57816001600160a01b0316634ce38b5f8483815181106130c9576130c9614173565b60200260200101516040518263ffffffff1660e01b81526004016130fc91906001600160a01b0391909116815260200190565b602060405180830381865afa158015613119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061313d9190614189565b6011828154811061315057613150614173565b600091825260209091200180546001600160a01b0319166001600160a01b039290921691909117905580613183816141bf565b91505061309f565b505050565b6040805160208101909152600081527601357c299a88ea76a58924d52ce4f26a85af186c2b9e748211156132255760405162461bcd60e51b815260206004820152603660248201527f63616e277420637265617465206669786964697479206e756d626572206c6172604482015275676572207468616e206d61784e65774669786564282960501b606482015260840161064a565b604051806020016040528069d3c21bcecceda100000084613246919061448f565b905292915050565b6001546040516956616c696461746f727360b01b60208201526000916001600160a01b03169063dcf0aaed90602a016128f4565b6040805160208101909152600081528251158061329e57508151155b156132b857506040805160208101909152600081526135ed565b815169d3c21bcecceda0ffffff19016132d25750816135ed565b825169d3c21bcecceda0ffffff19016132ec5750806135ed565b600069d3c21bcecceda100000061330285613bd5565b5161330d919061446d565b9050600061331a85613c14565b519050600069d3c21bcecceda100000061333386613bd5565b5161333e919061446d565b9050600061334b86613c14565b519050600061335a838661448f565b905084156133b3578261336d868361446d565b146133b35760405162461bcd60e51b81526020600482015260166024820152751bdd995c999b1bddc81e0c5e4c4819195d1958dd195960521b604482015260640161064a565b60006133c969d3c21bcecceda10000008361448f565b905081156134335769d3c21bcecceda10000006133e6838361446d565b146134335760405162461bcd60e51b815260206004820152601f60248201527f6f766572666c6f772078317931202a2066697865643120646574656374656400604482015260640161064a565b9050806000613442858761448f565b9050851561349b5784613455878361446d565b1461349b5760405162461bcd60e51b81526020600482015260166024820152751bdd995c999b1bddc81e0c9e4c4819195d1958dd195960521b604482015260640161064a565b60006134a7858961448f565b9050871561350057846134ba898361446d565b146135005760405162461bcd60e51b81526020600482015260166024820152751bdd995c999b1bddc81e0c5e4c8819195d1958dd195960521b604482015260640161064a565b61350f64e8d4a510008861446d565b965061352064e8d4a510008661446d565b9450600061352e868961448f565b905087156135875785613541898361446d565b146135875760405162461bcd60e51b81526020600482015260166024820152751bdd995c999b1bddc81e0c9e4c8819195d1958dd195960521b604482015260640161064a565b60408051602080820183528782528251908101909252848252906135ac908290613c5f565b90506135c681604051806020016040528086815250613c5f565b90506135e081604051806020016040528085815250613c5f565b9a50505050505050505050505b92915050565b80516000906135ed9069d3c21bcecceda10000009061446d565b600154604051674163636f756e747360c01b60208201526000916001600160a01b03169063dcf0aaed906028016128f4565b6001546040516a29ba30b13632aa37b5b2b760a91b60208201526000916001600160a01b03169063dcf0aaed90602b016128f4565b60028054036136c55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161064a565b60028055565b6001546040516b29b1b7b932a6b0b730b3b2b960a11b60208201526000916001600160a01b03169063dcf0aaed90602c016128f4565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b6001546040516b45706f63685265776172647360a01b60208201526000916001600160a01b03169063dcf0aaed90602c016128f4565b60008061379b6136cb565b905060006137a761324e565b9050600a60005b600854811015613970576000846001600160a01b031663e5079ddc600884815481106137dc576137dc614173565b60009182526020909120015460405160e083901b6001600160e01b03191681526001600160a01b039091166004820152602401602060405180830381865afa15801561382c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061385091906141a6565b90506000846001600160a01b031663087c344e6008858154811061387657613876614173565b600091825260209091200154600187015460405160e084901b6001600160e01b03191681526001600160a01b039092166004830152602482018690526044820152606401602060405180830381865afa1580156138d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138fb91906141a6565b905080601060006008868154811061391557613915614173565b60009182526020808320909101546001600160a01b031683528201929092526040018120805490919061394990849061441d565b909155506139599050818861441d565b965050508080613968906141bf565b9150506137ae565b508360000361397f5750505050565b604051633a90822b60e21b8152600481018590526001600160a01b0383169063ea4208ac90602401600060405180830381600087803b1580156139c157600080fd5b505af11580156139d5573d6000803e3d6000fd5b5050600754600092508291506001600160a01b031663efb7601d6139f761363f565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024016040805180830381865afa158015613a3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5e91906144a6565b9092509050600081613a70888561448f565b613a7a919061446d565b9050613a84613b95565b600154604051665265736572766560c81b60208201526001600160a01b0392831692630357371d92169063dcf0aaed90602701604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401613aeb91815260200190565b602060405180830381865afa158015613b08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b2c9190614189565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101849052604401600060405180830381600087803b158015613b7457600080fd5b505af1158015613b88573d6000803e3d6000fd5b5050505050505050505050565b6001546040517543656c6f556e72656c6561736564547265617375727960501b60208201526000916001600160a01b03169063dcf0aaed906036016128f4565b604080516020810190915260008152604051806020016040528069d3c21bcecceda1000000808560000151613c0a919061446d565b613246919061448f565b604080516020810190915260008152604051806020016040528069d3c21bcecceda1000000808560000151613c49919061446d565b613c53919061448f565b84516132469190614332565b60408051602081019091526000815281518351600091613c7e9161441d565b8451909150811015613cca5760405162461bcd60e51b8152602060048201526015602482015274185919081bdd995c999b1bddc819195d1958dd1959605a1b604482015260640161064a565b60408051602081019091529081529392505050565b828054828255906000526020600020908101928215613d34579160200282015b82811115613d3457825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613cff565b50613d40929150613d44565b5090565b5b80821115613d405760008155600101613d45565b6001600160a01b03811681146128c357600080fd5b600060208284031215613d8057600080fd5b8135613d8b81613d59565b9392505050565b600080600060608486031215613da757600080fd5b8335613db281613d59565b92506020840135613dc281613d59565b91506040840135613dd281613d59565b809150509250925092565b600060208284031215613def57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715613e3557613e35613df6565b604052919050565b600067ffffffffffffffff821115613e5757613e57613df6565b5060051b60200190565b600080600060608486031215613e7657600080fd5b833592506020808501359250604085013567ffffffffffffffff811115613e9c57600080fd5b8501601f81018713613ead57600080fd5b8035613ec0613ebb82613e3d565b613e0c565b81815260059190911b82018301908381019089831115613edf57600080fd5b928401925b82841015613f06578335613ef781613d59565b82529284019290840190613ee4565b80955050505050509250925092565b60008083601f840112613f2757600080fd5b50813567ffffffffffffffff811115613f3f57600080fd5b6020830191508360208260051b8501011115613f5a57600080fd5b9250929050565b60008060008060008060608789031215613f7a57600080fd5b863567ffffffffffffffff80821115613f9257600080fd5b613f9e8a838b01613f15565b90985096506020890135915080821115613fb757600080fd5b613fc38a838b01613f15565b90965094506040890135915080821115613fdc57600080fd5b50613fe989828a01613f15565b979a9699509497509295939492505050565b6020808252825182820181905260009190848201906040850190845b8181101561403c5783516001600160a01b031683529284019291840191600101614017565b50909695505050505050565b634e487b7160e01b600052602160045260246000fd5b60a081016003871061408057634e487b7160e01b600052602160045260246000fd5b95815260208101949094526040840192909252606083015260809091015290565b600080604083850312156140b457600080fd5b82356140bf81613d59565b946020939093013593505050565b60208082526022908201527f67726f7570206e6f742066726f6d2063757272656e7420656c65637465642073604082015261195d60f21b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b6000816141345761413461410f565b506000190190565b6020808252601c908201527f45706f63682073797374656d206e6f7420696e697469616c697a656400000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561419b57600080fd5b8151613d8b81613d59565b6000602082840312156141b857600080fd5b5051919050565b6000600182016141d1576141d161410f565b5060010190565b600082601f8301126141e957600080fd5b815160206141f9613ebb83613e3d565b82815260059290921b8401810191818101908684111561421857600080fd5b8286015b8481101561423c57805161422f81613d59565b835291830191830161421c565b509695505050505050565b600080600080600080600060e0888a03121561426257600080fd5b875167ffffffffffffffff8082111561427a57600080fd5b6142868b838c016141d8565b985060209150818a0151975060408a0151965060608a0151955060808a0151818111156142b257600080fd5b8a019050601f81018b136142c557600080fd5b80516142d3613ebb82613e3d565b81815260059190911b8201830190838101908d8311156142f257600080fd5b928401925b82841015614310578351825292840192908401906142f7565b809750505050505060a0880151915060c0880151905092959891949750929550565b818103818111156135ed576135ed61410f565b6000806040838503121561435857600080fd5b825161436381613d59565b6020939093015192949293505050565b60006020828403121561438557600080fd5b81518015158114613d8b57600080fd5b60208082526032908201527f456c6563746564206163636f756e747320616e64207369676e657273206f66206040820152713234b33332b932b73a103632b733ba34399760711b606082015260800190565b600080600080608085870312156143fd57600080fd5b505082516020840151604085015160609095015191969095509092509050565b808201808211156135ed576135ed61410f565b60006020828403121561444257600080fd5b815167ffffffffffffffff81111561445957600080fd5b614465848285016141d8565b949350505050565b60008261448a57634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176135ed576135ed61410f565b600080604083850312156144b957600080fd5b50508051602090910151909290915056fea264697066735822122069b5db76bab2d7411c2a04303e2c7ca0c9f04797893d2f480f61d2b8e9c5e9bb64736f6c63430008130033

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

0000000000000000000000000000000000000000000000000000000000000001

-----Decoded View---------------
Arg [0] : test (bool): True

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


Block Transaction Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
0x0007fce3FE6D2F84f2EADDcdaC8e8AA1605eDb5B
Loading...
Loading
Loading...
Loading
Loading...
Loading

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.