ETH Price: $2,358.01 (+1.29%)
Gas: 2.76 Gwei

Contract

0xc2731fb2823af3Efc2694c9bC86F444d5c5bb4Dc
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Restore Reward E...206322092024-08-29 5:39:1115 days ago1724909951IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.000043231.17794779
Restore Reward E...201519982024-06-23 4:25:1182 days ago1719116711IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.000070631.92461089
Restore Reward E...199367092024-05-24 2:18:35112 days ago1716517115IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.000217545.92711669
Restore Reward E...197362652024-04-26 1:29:35140 days ago1714094975IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.000229746.25948838
Restore Reward E...194257392024-03-13 11:05:35184 days ago1710327935IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0024293566.18955383
Restore Reward E...194257382024-03-13 11:05:23184 days ago1710327923IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0024763967.47115728
Restore Reward E...193721382024-03-05 23:05:35191 days ago1709679935IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.002591770.61285255
Restore Reward E...193721372024-03-05 23:05:23191 days ago1709679923IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0026457872.0862748
Restore Reward E...193381252024-03-01 5:05:23196 days ago1709269523IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0018284249.8166624
Restore Reward E...193381252024-03-01 5:05:23196 days ago1709269523IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0018284249.8166624
Restore Reward E...191963882024-02-10 8:00:47216 days ago1707552047IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0012786234.83704703
Restore Reward E...187351572023-12-07 14:58:23280 days ago1701961103IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0023143263.05546574
Restore Reward E...187348292023-12-07 13:51:23281 days ago1701957083IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0016119243.91796305
Transfer Ownersh...156395222022-09-29 14:13:35714 days ago1664460815IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0005118417.89662929
Transfer Chaosne...156394802022-09-29 14:05:11715 days ago1664460311IN
0xc2731fb2...d5c5bb4Dc
0 ETH0.0005947920.86856409
0x60e06040156394782022-09-29 14:04:47715 days ago1664460287IN
 Create: SortitionPool
0 ETH0.0561204722.56795561

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SortitionPool

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 16 : SortitionPool.sol
pragma solidity 0.8.17;

import "@thesis/solidity-contracts/contracts/token/IERC20WithPermit.sol";
import "@thesis/solidity-contracts/contracts/token/IReceiveApproval.sol";

import "@openzeppelin/contracts/access/Ownable.sol";

import "./RNG.sol";
import "./SortitionTree.sol";
import "./Rewards.sol";
import "./Chaosnet.sol";

/// @title Sortition Pool
/// @notice A logarithmic data structure used to store the pool of eligible
/// operators weighted by their stakes. It allows to select a group of operators
/// based on the provided pseudo-random seed.
contract SortitionPool is
  SortitionTree,
  Rewards,
  Ownable,
  Chaosnet,
  IReceiveApproval
{
  using Branch for uint256;
  using Leaf for uint256;
  using Position for uint256;

  IERC20WithPermit public immutable rewardToken;

  uint256 public immutable poolWeightDivisor;

  bool public isLocked;

  event IneligibleForRewards(uint32[] ids, uint256 until);

  event RewardEligibilityRestored(address indexed operator, uint32 indexed id);

  /// @notice Reverts if called while pool is locked.
  modifier onlyUnlocked() {
    require(!isLocked, "Sortition pool locked");
    _;
  }

  /// @notice Reverts if called while pool is unlocked.
  modifier onlyLocked() {
    require(isLocked, "Sortition pool unlocked");
    _;
  }

  constructor(IERC20WithPermit _rewardToken, uint256 _poolWeightDivisor) {
    rewardToken = _rewardToken;
    poolWeightDivisor = _poolWeightDivisor;
  }

  function receiveApproval(
    address sender,
    uint256 amount,
    address token,
    bytes calldata
  ) external override {
    require(token == address(rewardToken), "Unsupported token");
    rewardToken.transferFrom(sender, address(this), amount);
    Rewards.addRewards(uint96(amount), uint32(root.sumWeight()));
  }

  /// @notice Withdraws all available rewards for the given operator to the
  ///         given beneficiary.
  /// @dev Can be called only be the owner. Does not validate if the provided
  ///      beneficiary is associated with the provided operator - this needs to
  ///      be done by the owner calling this function.
  /// @return The amount of rewards withdrawn in this call.
  function withdrawRewards(address operator, address beneficiary)
    public
    onlyOwner
    returns (uint96)
  {
    uint32 id = getOperatorID(operator);
    Rewards.updateOperatorRewards(id, uint32(getPoolWeight(operator)));
    uint96 earned = Rewards.withdrawOperatorRewards(id);
    rewardToken.transfer(beneficiary, uint256(earned));
    return earned;
  }

  /// @notice Withdraws rewards not allocated to operators marked as ineligible
  ///         to the given recipient address.
  /// @dev Can be called only by the owner.
  function withdrawIneligible(address recipient) public onlyOwner {
    uint96 earned = Rewards.withdrawIneligibleRewards();
    rewardToken.transfer(recipient, uint256(earned));
  }

  /// @notice Locks the sortition pool. In locked state, members cannot be
  ///         inserted and removed from the pool. Members statuses cannot
  ///         be updated as well.
  /// @dev Can be called only by the contract owner.
  function lock() public onlyOwner {
    isLocked = true;
  }

  /// @notice Unlocks the sortition pool. Removes all restrictions set by
  ///         the `lock` method.
  /// @dev Can be called only by the contract owner.
  function unlock() public onlyOwner {
    isLocked = false;
  }

  /// @notice Inserts an operator to the pool. Reverts if the operator is
  /// already present. Reverts if the operator is not eligible because of their
  /// authorized stake. Reverts if the chaosnet is active and the operator is
  /// not a beta operator.
  /// @dev Can be called only by the contract owner.
  /// @param operator Address of the inserted operator.
  /// @param authorizedStake Inserted operator's authorized stake for the application.
  function insertOperator(address operator, uint256 authorizedStake)
    public
    onlyOwner
    onlyUnlocked
  {
    uint256 weight = getWeight(authorizedStake);
    require(weight > 0, "Operator not eligible");

    if (isChaosnetActive) {
      require(isBetaOperator[operator], "Not beta operator for chaosnet");
    }

    _insertOperator(operator, weight);
    uint32 id = getOperatorID(operator);
    Rewards.updateOperatorRewards(id, uint32(weight));
  }

  /// @notice Update the operator's weight if present and eligible,
  /// or remove from the pool if present and ineligible.
  /// @dev Can be called only by the contract owner.
  /// @param operator Address of the updated operator.
  /// @param authorizedStake Operator's authorized stake for the application.
  function updateOperatorStatus(address operator, uint256 authorizedStake)
    public
    onlyOwner
    onlyUnlocked
  {
    uint256 weight = getWeight(authorizedStake);

    uint32 id = getOperatorID(operator);
    Rewards.updateOperatorRewards(id, uint32(weight));

    if (weight == 0) {
      _removeOperator(operator);
    } else {
      updateOperator(operator, weight);
    }
  }

  /// @notice Set the given operators as ineligible for rewards.
  ///         The operators can restore their eligibility at the given time.
  function setRewardIneligibility(uint32[] calldata operators, uint256 until)
    public
    onlyOwner
  {
    Rewards.setIneligible(operators, until);
    emit IneligibleForRewards(operators, until);
  }

  /// @notice Restores reward eligibility for the operator.
  function restoreRewardEligibility(address operator) public {
    uint32 id = getOperatorID(operator);
    Rewards.restoreEligibility(id);
    emit RewardEligibilityRestored(operator, id);
  }

  /// @notice Returns whether the operator is eligible for rewards or not.
  function isEligibleForRewards(address operator) public view returns (bool) {
    uint32 id = getOperatorID(operator);
    return Rewards.isEligibleForRewards(id);
  }

  /// @notice Returns the time the operator's reward eligibility can be restored.
  function rewardsEligibilityRestorableAt(address operator)
    public
    view
    returns (uint256)
  {
    uint32 id = getOperatorID(operator);
    return Rewards.rewardsEligibilityRestorableAt(id);
  }

  /// @notice Returns whether the operator is able to restore their eligibility
  ///         for rewards right away.
  function canRestoreRewardEligibility(address operator)
    public
    view
    returns (bool)
  {
    uint32 id = getOperatorID(operator);
    return Rewards.canRestoreRewardEligibility(id);
  }

  /// @notice Returns the amount of rewards withdrawable for the given operator.
  function getAvailableRewards(address operator) public view returns (uint96) {
    uint32 id = getOperatorID(operator);
    return availableRewards(id);
  }

  /// @notice Return whether the operator is present in the pool.
  function isOperatorInPool(address operator) public view returns (bool) {
    return getFlaggedLeafPosition(operator) != 0;
  }

  /// @notice Return whether the operator's weight in the pool
  /// matches their eligible weight.
  function isOperatorUpToDate(address operator, uint256 authorizedStake)
    public
    view
    returns (bool)
  {
    return getWeight(authorizedStake) == getPoolWeight(operator);
  }

  /// @notice Return the weight of the operator in the pool,
  /// which may or may not be out of date.
  function getPoolWeight(address operator) public view returns (uint256) {
    uint256 flaggedPosition = getFlaggedLeafPosition(operator);
    if (flaggedPosition == 0) {
      return 0;
    } else {
      uint256 leafPosition = flaggedPosition.unsetFlag();
      uint256 leafWeight = getLeafWeight(leafPosition);
      return leafWeight;
    }
  }

  /// @notice Selects a new group of operators of the provided size based on
  /// the provided pseudo-random seed. At least one operator has to be
  /// registered in the pool, otherwise the function fails reverting the
  /// transaction.
  /// @param groupSize Size of the requested group
  /// @param seed Pseudo-random number used to select operators to group
  /// @return selected Members of the selected group
  function selectGroup(uint256 groupSize, bytes32 seed)
    public
    view
    onlyLocked
    returns (uint32[] memory)
  {
    uint256 _root = root;

    bytes32 rngState = seed;
    uint256 rngRange = _root.sumWeight();
    require(rngRange > 0, "Not enough operators in pool");
    uint256 currentIndex;

    uint256 bits = RNG.bitsRequired(rngRange);

    uint32[] memory selected = new uint32[](groupSize);

    for (uint256 i = 0; i < groupSize; i++) {
      (currentIndex, rngState) = RNG.getIndex(rngRange, rngState, bits);

      uint256 leafPosition = pickWeightedLeaf(currentIndex, _root);

      uint256 leaf = leaves[leafPosition];
      selected[i] = leaf.id();
    }
    return selected;
  }

  function getWeight(uint256 authorization) internal view returns (uint256) {
    return authorization / poolWeightDivisor;
  }
}

File 2 of 16 : IERC20WithPermit.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import "./IApproveAndCall.sol";

/// @title  IERC20WithPermit
/// @notice Burnable ERC20 token with EIP2612 permit functionality. User can
///         authorize a transfer of their token with a signature conforming
///         EIP712 standard instead of an on-chain transaction from their
///         address. Anyone can submit this signature on the user's behalf by
///         calling the permit function, as specified in EIP2612 standard,
///         paying gas fees, and possibly performing other actions in the same
///         transaction.
interface IERC20WithPermit is IERC20, IERC20Metadata, IApproveAndCall {
    /// @notice EIP2612 approval made with secp256k1 signature.
    ///         Users can authorize a transfer of their tokens with a signature
    ///         conforming EIP712 standard, rather than an on-chain transaction
    ///         from their address. Anyone can submit this signature on the
    ///         user's behalf by calling the permit function, paying gas fees,
    ///         and possibly performing other actions in the same transaction.
    /// @dev    The deadline argument can be set to `type(uint256).max to create
    ///         permits that effectively never expire.
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /// @notice Destroys `amount` tokens from the caller.
    function burn(uint256 amount) external;

    /// @notice Destroys `amount` of tokens from `account`, deducting the amount
    ///         from caller's allowance.
    function burnFrom(address account, uint256 amount) external;

    /// @notice Returns hash of EIP712 Domain struct with the token name as
    ///         a signing domain and token contract as a verifying contract.
    ///         Used to construct EIP2612 signature provided to `permit`
    ///         function.
    /* solhint-disable-next-line func-name-mixedcase */
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice Returns the current nonce for EIP2612 permission for the
    ///         provided token owner for a replay protection. Used to construct
    ///         EIP2612 signature provided to `permit` function.
    function nonce(address owner) external view returns (uint256);

    /// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612
    ///         signature provided to `permit` function.
    /* solhint-disable-next-line func-name-mixedcase */
    function PERMIT_TYPEHASH() external pure returns (bytes32);
}

File 3 of 16 : IReceiveApproval.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

/// @notice An interface that should be implemented by contracts supporting
///         `approveAndCall`/`receiveApproval` pattern.
interface IReceiveApproval {
    /// @notice Receives approval to spend tokens. Called as a result of
    ///         `approveAndCall` call on the token.
    function receiveApproval(
        address from,
        uint256 amount,
        address token,
        bytes calldata extraData
    ) external;
}

File 4 of 16 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _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);
    }
}

File 5 of 16 : RNG.sol
pragma solidity 0.8.17;

import "./Leaf.sol";
import "./Constants.sol";

library RNG {
  /// @notice Get an index in the range `[0 .. range-1]`
  /// and the new state of the RNG,
  /// using the provided `state` of the RNG.
  ///
  /// @param range The upper bound of the index, exclusive.
  ///
  /// @param state The previous state of the RNG.
  /// The initial state needs to be obtained
  /// from a trusted randomness oracle (the random beacon),
  /// or from a chain of earlier calls to `RNG.getIndex()`
  /// on an originally trusted seed.
  ///
  /// @dev Calculates the number of bits required for the desired range,
  /// takes the least significant bits of `state`
  /// and checks if the obtained index is within the desired range.
  /// The original state is hashed with `keccak256` to get a new state.
  /// If the index is outside the range,
  /// the function retries until it gets a suitable index.
  ///
  /// @return index A random integer between `0` and `range - 1`, inclusive.
  ///
  /// @return newState The new state of the RNG.
  /// When `getIndex()` is called one or more times,
  /// care must be taken to always use the output `state`
  /// of the most recent call as the input `state` of a subsequent call.
  /// At the end of a transaction calling `RNG.getIndex()`,
  /// the previous stored state must be overwritten with the latest output.
  function getIndex(
    uint256 range,
    bytes32 state,
    uint256 bits
  ) internal view returns (uint256, bytes32) {
    bool found = false;
    uint256 index = 0;
    bytes32 newState = state;
    while (!found) {
      index = truncate(bits, uint256(newState));
      newState = keccak256(abi.encodePacked(newState, address(this)));
      if (index < range) {
        found = true;
      }
    }
    return (index, newState);
  }

  /// @notice Calculate how many bits are required
  /// for an index in the range `[0 .. range-1]`.
  ///
  /// @param range The upper bound of the desired range, exclusive.
  ///
  /// @return uint The smallest number of bits
  /// that can contain the number `range-1`.
  function bitsRequired(uint256 range) internal pure returns (uint256) {
    unchecked {
      if (range == 1) {
        return 0;
      }

      uint256 bits = Constants.WEIGHT_WIDTH - 1;

      // Left shift by `bits`,
      // so we have a 1 in the (bits + 1)th least significant bit
      // and 0 in other bits.
      // If this number is equal or greater than `range`,
      // the range [0, range-1] fits in `bits` bits.
      //
      // Because we loop from high bits to low bits,
      // we find the highest number of bits that doesn't fit the range,
      // and return that number + 1.
      while (1 << bits >= range) {
        bits--;
      }

      return bits + 1;
    }
  }

  /// @notice Truncate `input` to the `bits` least significant bits.
  function truncate(uint256 bits, uint256 input)
    internal
    pure
    returns (uint256)
  {
    unchecked {
      return input & ((1 << bits) - 1);
    }
  }
}

File 6 of 16 : SortitionTree.sol
pragma solidity 0.8.17;

import "./Branch.sol";
import "./Position.sol";
import "./Leaf.sol";
import "./Constants.sol";

contract SortitionTree {
  using Branch for uint256;
  using Position for uint256;
  using Leaf for uint256;

  // implicit tree
  // root 8
  // level2 64
  // level3 512
  // level4 4k
  // level5 32k
  // level6 256k
  // level7 2M
  uint256 internal root;

  // A 2-index mapping from layer => (index (0-index) => branch). For example,
  // to access the 6th branch in the 2nd layer (right below the root node; the
  // first branch layer), call branches[2][5]. Mappings are used in place of
  // arrays for efficiency. The root is the first layer, the branches occupy
  // layers 2 through 7, and layer 8 is for the leaves. Following this
  // convention, the first index in `branches` is `2`, and the last index is
  // `7`.
  mapping(uint256 => mapping(uint256 => uint256)) internal branches;

  // A 0-index mapping from index => leaf, acting as an array. For example, to
  // access the 42nd leaf, call leaves[41].
  mapping(uint256 => uint256) internal leaves;

  // the flagged (see setFlag() and unsetFlag() in Position.sol) positions
  // of all operators present in the pool
  mapping(address => uint256) internal flaggedLeafPosition;

  // the leaf after the rightmost occupied leaf of each stack
  uint256 internal rightmostLeaf;

  // the empty leaves in each stack
  // between 0 and the rightmost occupied leaf
  uint256[] internal emptyLeaves;

  // Each operator has an uint32 ID number
  // which is allocated when they first join the pool
  // and remains unchanged even if they leave and rejoin the pool.
  mapping(address => uint32) internal operatorID;

  // The idAddress array records the address corresponding to each ID number.
  // The ID number 0 is initialized with a zero address and is not used.
  address[] internal idAddress;

  constructor() {
    root = 0;
    rightmostLeaf = 0;
    idAddress.push();
  }

  /// @notice Return the ID number of the given operator address. An ID number
  /// of 0 means the operator has not been allocated an ID number yet.
  /// @param operator Address of the operator.
  /// @return the ID number of the given operator address
  function getOperatorID(address operator) public view returns (uint32) {
    return operatorID[operator];
  }

  /// @notice Get the operator address corresponding to the given ID number. A
  /// zero address means the ID number has not been allocated yet.
  /// @param id ID of the operator
  /// @return the address of the operator
  function getIDOperator(uint32 id) public view returns (address) {
    return idAddress.length > id ? idAddress[id] : address(0);
  }

  /// @notice Gets the operator addresses corresponding to the given ID
  /// numbers. A zero address means the ID number has not been allocated yet.
  /// This function works just like getIDOperator except that it allows to fetch
  /// operator addresses for multiple IDs in one call.
  /// @param ids the array of the operator ids
  /// @return an array of the associated operator addresses
  function getIDOperators(uint32[] calldata ids)
    public
    view
    returns (address[] memory)
  {
    uint256 idCount = idAddress.length;

    address[] memory operators = new address[](ids.length);
    for (uint256 i = 0; i < ids.length; i++) {
      uint32 id = ids[i];
      operators[i] = idCount > id ? idAddress[id] : address(0);
    }
    return operators;
  }

  /// @notice Checks if operator is already registered in the pool.
  /// @param operator the address of the operator
  /// @return whether or not the operator is already registered in the pool
  function isOperatorRegistered(address operator) public view returns (bool) {
    return getFlaggedLeafPosition(operator) != 0;
  }

  /// @notice Sum the number of operators in each trunk.
  /// @return the number of operators in the pool
  function operatorsInPool() public view returns (uint256) {
    // Get the number of leaves that might be occupied;
    // if `rightmostLeaf` equals `firstLeaf()` the tree must be empty,
    // otherwise the difference between these numbers
    // gives the number of leaves that may be occupied.
    uint256 nPossiblyUsedLeaves = rightmostLeaf;
    // Get the number of empty leaves
    // not accounted for by the `rightmostLeaf`
    uint256 nEmptyLeaves = emptyLeaves.length;

    return (nPossiblyUsedLeaves - nEmptyLeaves);
  }

  /// @notice Convenience method to return the total weight of the pool
  /// @return the total weight of the pool
  function totalWeight() public view returns (uint256) {
    return root.sumWeight();
  }

  /// @notice Give the operator a new ID number.
  /// Does not check if the operator already has an ID number.
  /// @param operator the address of the operator
  /// @return a new ID for that operator
  function allocateOperatorID(address operator) internal returns (uint256) {
    uint256 id = idAddress.length;

    require(id <= type(uint32).max, "Pool capacity exceeded");

    operatorID[operator] = uint32(id);
    idAddress.push(operator);
    return id;
  }

  /// @notice Inserts an operator into the sortition pool
  /// @param operator the address of an operator to insert
  /// @param weight how much weight that operator has in the pool
  function _insertOperator(address operator, uint256 weight) internal {
    require(
      !isOperatorRegistered(operator),
      "Operator is already registered in the pool"
    );

    // Fetch the operator's ID, and if they don't have one, allocate them one.
    uint256 id = getOperatorID(operator);
    if (id == 0) {
      id = allocateOperatorID(operator);
    }

    // Determine which leaf to insert them into
    uint256 position = getEmptyLeafPosition();
    // Record the block the operator was inserted in
    uint256 theLeaf = Leaf.make(operator, block.number, id);

    // Update the leaf, and propagate the weight changes all the way up to the
    // root.
    root = setLeaf(position, theLeaf, weight, root);

    // Without position flags,
    // the position 0x000000 would be treated as empty
    flaggedLeafPosition[operator] = position.setFlag();
  }

  /// @notice Remove an operator (and their weight) from the pool.
  /// @param operator the address of the operator to remove
  function _removeOperator(address operator) internal {
    uint256 flaggedPosition = getFlaggedLeafPosition(operator);
    require(flaggedPosition != 0, "Operator is not registered in the pool");
    uint256 unflaggedPosition = flaggedPosition.unsetFlag();

    // Update the leaf, and propagate the weight changes all the way up to the
    // root.
    root = removeLeaf(unflaggedPosition, root);
    removeLeafPositionRecord(operator);
  }

  /// @notice Update an operator's weight in the pool.
  /// @param operator the address of the operator to update
  /// @param weight the new weight
  function updateOperator(address operator, uint256 weight) internal {
    require(
      isOperatorRegistered(operator),
      "Operator is not registered in the pool"
    );

    uint256 flaggedPosition = getFlaggedLeafPosition(operator);
    uint256 unflaggedPosition = flaggedPosition.unsetFlag();
    root = updateLeaf(unflaggedPosition, weight, root);
  }

  /// @notice Helper method to remove a leaf position record for an operator.
  /// @param operator the address of the operator to remove the record for
  function removeLeafPositionRecord(address operator) internal {
    flaggedLeafPosition[operator] = 0;
  }

  /// @notice Removes the data and weight from a particular leaf.
  /// @param position the leaf index to remove
  /// @param _root the root node containing the leaf
  /// @return the updated root node
  function removeLeaf(uint256 position, uint256 _root)
    internal
    returns (uint256)
  {
    uint256 rightmostSubOne = rightmostLeaf - 1;
    bool isRightmost = position == rightmostSubOne;

    // Clears out the data in the leaf node, and then propagates the weight
    // changes all the way up to the root.
    uint256 newRoot = setLeaf(position, 0, 0, _root);

    // Infer if need to fall back on emptyLeaves yet
    if (isRightmost) {
      rightmostLeaf = rightmostSubOne;
    } else {
      emptyLeaves.push(position);
    }
    return newRoot;
  }

  /// @notice Updates the tree to give a particular leaf a new weight.
  /// @param position the index of the leaf to update
  /// @param weight the new weight
  /// @param _root the root node containing the leaf
  /// @return the updated root node
  function updateLeaf(
    uint256 position,
    uint256 weight,
    uint256 _root
  ) internal returns (uint256) {
    if (getLeafWeight(position) != weight) {
      return updateTree(position, weight, _root);
    } else {
      return _root;
    }
  }

  /// @notice Places a leaf into a particular position, with a given weight and
  /// propagates that change.
  /// @param position the index to place the leaf in
  /// @param theLeaf the new leaf to place in the position
  /// @param leafWeight the weight of the leaf
  /// @param _root the root containing the new leaf
  /// @return the updated root node
  function setLeaf(
    uint256 position,
    uint256 theLeaf,
    uint256 leafWeight,
    uint256 _root
  ) internal returns (uint256) {
    // set leaf
    leaves[position] = theLeaf;

    return (updateTree(position, leafWeight, _root));
  }

  /// @notice Propagates a weight change at a position through the tree,
  /// eventually returning the updated root.
  /// @param position the index of leaf to update
  /// @param weight the new weight of the leaf
  /// @param _root the root node containing the leaf
  /// @return the updated root node
  function updateTree(
    uint256 position,
    uint256 weight,
    uint256 _root
  ) internal returns (uint256) {
    uint256 childSlot;
    uint256 treeNode;
    uint256 newNode;
    uint256 nodeWeight = weight;

    uint256 parent = position;
    // set levels 7 to 2
    for (uint256 level = Constants.LEVELS; level >= 2; level--) {
      childSlot = parent.slot();
      parent = parent.parent();
      treeNode = branches[level][parent];
      newNode = treeNode.setSlot(childSlot, nodeWeight);
      branches[level][parent] = newNode;
      nodeWeight = newNode.sumWeight();
    }

    // set level Root
    childSlot = parent.slot();
    return _root.setSlot(childSlot, nodeWeight);
  }

  /// @notice Retrieves the next available empty leaf position. Tries to fill
  /// left to right first, ignoring leaf removals, and then fills
  /// most-recent-removals first.
  /// @return the position of the empty leaf
  function getEmptyLeafPosition() internal returns (uint256) {
    uint256 rLeaf = rightmostLeaf;
    bool spaceOnRight = (rLeaf + 1) < Constants.POOL_CAPACITY;
    if (spaceOnRight) {
      rightmostLeaf = rLeaf + 1;
      return rLeaf;
    } else {
      uint256 emptyLeafCount = emptyLeaves.length;
      require(emptyLeafCount > 0, "Pool is full");
      uint256 emptyLeaf = emptyLeaves[emptyLeafCount - 1];
      emptyLeaves.pop();
      return emptyLeaf;
    }
  }

  /// @notice Gets the flagged leaf position for an operator.
  /// @param operator the address of the operator
  /// @return the leaf position of that operator
  function getFlaggedLeafPosition(address operator)
    internal
    view
    returns (uint256)
  {
    return flaggedLeafPosition[operator];
  }

  /// @notice Gets the weight of a leaf at a particular position.
  /// @param position the index of the leaf
  /// @return the weight of the leaf at that position
  function getLeafWeight(uint256 position) internal view returns (uint256) {
    uint256 slot = position.slot();
    uint256 parent = position.parent();

    // A leaf's weight information is stored a 32-bit slot in the branch layer
    // directly above the leaf layer. To access it, we calculate that slot and
    // parent position, and always know the hard-coded layer index.
    uint256 node = branches[Constants.LEVELS][parent];
    return node.getSlot(slot);
  }

  /// @notice Picks a leaf given a random index.
  /// @param index a number in `[0, _root.totalWeight())` used to decide
  /// between leaves
  /// @param _root the root of the tree
  function pickWeightedLeaf(uint256 index, uint256 _root)
    internal
    view
    returns (uint256 leafPosition)
  {
    uint256 currentIndex = index;
    uint256 currentNode = _root;
    uint256 currentPosition = 0;
    uint256 currentSlot;

    require(index < currentNode.sumWeight(), "Index exceeds weight");

    // get root slot
    (currentSlot, currentIndex) = currentNode.pickWeightedSlot(currentIndex);

    // get slots from levels 2 to 7
    for (uint256 level = 2; level <= Constants.LEVELS; level++) {
      currentPosition = currentPosition.child(currentSlot);
      currentNode = branches[level][currentPosition];
      (currentSlot, currentIndex) = currentNode.pickWeightedSlot(currentIndex);
    }

    // get leaf position
    leafPosition = currentPosition.child(currentSlot);
  }
}

File 7 of 16 : Rewards.sol
pragma solidity 0.8.17;

/// @title Rewards
/// @notice Rewards are allocated proportionally to operators
/// present in the pool at payout based on their weight in the pool.
///
/// To facilitate this, we use a global accumulator value
/// to track the total rewards one unit of weight would've earned
/// since the creation of the pool.
///
/// Whenever a reward is paid, the accumulator is increased
/// by the size of the reward divided by the total weight
/// of all eligible operators in the pool.
///
/// Each operator has an individual accumulator value,
/// set to equal the global accumulator when the operator joins the pool.
/// This accumulator reflects the amount of rewards
/// that have already been accounted for with that operator.
///
/// Whenever an operator's weight in the pool changes,
/// we can update the amount of rewards the operator has earned
/// by subtracting the operator's accumulator from the global accumulator.
/// This gives us the amount of rewards one unit of weight has earned
/// since the last time the operator's rewards have been updated.
/// Then we multiply that by the operator's previous (pre-change) weight
/// to determine how much rewards in total the operator has earned,
/// and add this to the operator's earned rewards.
/// Finally, we set the operator's accumulator to the global accumulator value.
contract Rewards {
  struct OperatorRewards {
    // The state of the global accumulator
    // when the operator's rewards were last updated
    uint96 accumulated;
    // The amount of rewards collected by the operator after the latest update.
    // The amount the operator could withdraw may equal `available`
    // or it may be greater, if more rewards have been paid in since then.
    // To evaulate the most recent amount including rewards potentially paid
    // since the last update, use `availableRewards` function.
    uint96 available;
    // If nonzero, the operator is ineligible for rewards
    // and may only re-enable rewards after the specified timestamp.
    // XXX: unsigned 32-bit integer unix seconds, will break around 2106
    uint32 ineligibleUntil;
    // Locally cached weight of the operator,
    // used to reduce the cost of setting operators ineligible.
    uint32 weight;
  }

  // The global accumulator of how much rewards
  // a hypothetical operator of weight 1 would have earned
  // since the creation of the pool.
  uint96 internal globalRewardAccumulator;
  // If the amount of reward tokens paid in
  // does not divide cleanly by pool weight,
  // the difference is recorded as rounding dust
  // and added to the next reward.
  uint96 internal rewardRoundingDust;

  // The amount of rewards that would've been earned by ineligible operators
  // had they not been ineligible.
  uint96 public ineligibleEarnedRewards;

  // Ineligibility times are calculated from this offset,
  // set at contract creation.
  uint256 internal immutable ineligibleOffsetStart;

  mapping(uint32 => OperatorRewards) internal operatorRewards;

  constructor() {
    // solhint-disable-next-line not-rely-on-time
    ineligibleOffsetStart = block.timestamp;
  }

  /// @notice Return whether the operator is eligible for rewards or not.
  function isEligibleForRewards(uint32 operator) internal view returns (bool) {
    return operatorRewards[operator].ineligibleUntil == 0;
  }

  /// @notice Return the time the operator's reward eligibility can be restored.
  function rewardsEligibilityRestorableAt(uint32 operator)
    internal
    view
    returns (uint256)
  {
    uint32 until = operatorRewards[operator].ineligibleUntil;
    require(until != 0, "Operator already eligible");
    return (uint256(until) + ineligibleOffsetStart);
  }

  /// @notice Return whether the operator is able to restore their eligibility
  ///         for rewards right away.
  function canRestoreRewardEligibility(uint32 operator)
    internal
    view
    returns (bool)
  {
    // solhint-disable-next-line not-rely-on-time
    return rewardsEligibilityRestorableAt(operator) <= block.timestamp;
  }

  /// @notice Internal function for updating the global state of rewards.
  function addRewards(uint96 rewardAmount, uint32 currentPoolWeight) internal {
    require(currentPoolWeight > 0, "No recipients in pool");

    uint96 totalAmount = rewardAmount + rewardRoundingDust;
    uint96 perWeightReward = totalAmount / currentPoolWeight;
    uint96 newRoundingDust = totalAmount % currentPoolWeight;

    globalRewardAccumulator += perWeightReward;
    rewardRoundingDust = newRoundingDust;
  }

  /// @notice Internal function for updating the operator's reward state.
  function updateOperatorRewards(uint32 operator, uint32 newWeight) internal {
    uint96 acc = globalRewardAccumulator;
    OperatorRewards memory o = operatorRewards[operator];
    uint96 accruedRewards = (acc - o.accumulated) * uint96(o.weight);
    if (o.ineligibleUntil == 0) {
      // If operator is not ineligible, update their earned rewards
      o.available += accruedRewards;
    } else {
      // If ineligible, put the rewards into the ineligible pot
      ineligibleEarnedRewards += accruedRewards;
    }
    // In any case, update their accumulator and weight
    o.accumulated = acc;
    o.weight = newWeight;
    operatorRewards[operator] = o;
  }

  /// @notice Set the amount of withdrawable tokens to zero
  /// and return the previous withdrawable amount.
  /// @dev Does not update the withdrawable amount,
  /// but should usually be accompanied by an update.
  function withdrawOperatorRewards(uint32 operator)
    internal
    returns (uint96 withdrawable)
  {
    OperatorRewards storage o = operatorRewards[operator];
    withdrawable = o.available;
    o.available = 0;
  }

  /// @notice Set the amount of ineligible-earned tokens to zero
  /// and return the previous amount.
  function withdrawIneligibleRewards() internal returns (uint96 withdrawable) {
    withdrawable = ineligibleEarnedRewards;
    ineligibleEarnedRewards = 0;
  }

  /// @notice Set the given operators as ineligible for rewards.
  /// The operators can restore their eligibility at the given time.
  function setIneligible(uint32[] memory operators, uint256 until) internal {
    OperatorRewards memory o = OperatorRewards(0, 0, 0, 0);
    uint96 globalAcc = globalRewardAccumulator;
    uint96 accrued = 0;
    // Record ineligibility as seconds after contract creation
    uint32 _until = uint32(until - ineligibleOffsetStart);

    for (uint256 i = 0; i < operators.length; i++) {
      uint32 operator = operators[i];
      OperatorRewards storage r = operatorRewards[operator];
      o.available = r.available;
      o.accumulated = r.accumulated;
      o.ineligibleUntil = r.ineligibleUntil;
      o.weight = r.weight;

      if (o.ineligibleUntil != 0) {
        // If operator is already ineligible,
        // don't earn rewards or shorten its ineligibility
        if (o.ineligibleUntil < _until) {
          o.ineligibleUntil = _until;
        }
      } else {
        // The operator becomes ineligible -> earn rewards
        o.ineligibleUntil = _until;
        accrued = (globalAcc - o.accumulated) * uint96(o.weight);
        o.available += accrued;
      }
      o.accumulated = globalAcc;

      r.available = o.available;
      r.accumulated = o.accumulated;
      r.ineligibleUntil = o.ineligibleUntil;
      r.weight = o.weight;
    }
  }

  /// @notice Restore the given operator's eligibility for rewards.
  function restoreEligibility(uint32 operator) internal {
    // solhint-disable-next-line not-rely-on-time
    require(canRestoreRewardEligibility(operator), "Operator still ineligible");
    uint96 acc = globalRewardAccumulator;
    OperatorRewards memory o = operatorRewards[operator];
    uint96 accruedRewards = (acc - o.accumulated) * uint96(o.weight);
    ineligibleEarnedRewards += accruedRewards;
    o.accumulated = acc;
    o.ineligibleUntil = 0;
    operatorRewards[operator] = o;
  }

  /// @notice Returns the amount of rewards currently available for withdrawal
  ///         for the given operator.
  function availableRewards(uint32 operator) internal view returns (uint96) {
    uint96 acc = globalRewardAccumulator;
    OperatorRewards memory o = operatorRewards[operator];
    if (o.ineligibleUntil == 0) {
      // If operator is not ineligible, calculate newly accrued rewards and add
      // them to the available ones, calculated during the last update.
      uint96 accruedRewards = (acc - o.accumulated) * uint96(o.weight);
      return o.available + accruedRewards;
    } else {
      // If ineligible, return only the rewards calculated during the last
      // update.
      return o.available;
    }
  }
}

File 8 of 16 : Chaosnet.sol
pragma solidity 0.8.17;

/// @title Chaosnet
/// @notice This is a beta staker program for stakers willing to go the extra
/// mile with monitoring, share their logs with the dev team, and allow to more
/// carefully monitor the bootstrapping network. As the network matures, the
/// beta program will be ended.
contract Chaosnet {
  /// @notice Indicates if the chaosnet is active. The chaosnet is active
  /// after the contract deployment and can be ended with a call to
  /// `deactivateChaosnet()`. Once deactivated chaosnet can not be activated
  /// again.
  bool public isChaosnetActive;

  /// @notice Indicates if the given operator is a beta operator for chaosnet.
  mapping(address => bool) public isBetaOperator;

  /// @notice Address controlling chaosnet status and beta operator addresses.
  address public chaosnetOwner;

  event BetaOperatorsAdded(address[] operators);

  event ChaosnetOwnerRoleTransferred(
    address oldChaosnetOwner,
    address newChaosnetOwner
  );

  event ChaosnetDeactivated();

  constructor() {
    _transferChaosnetOwner(msg.sender);
    isChaosnetActive = true;
  }

  modifier onlyChaosnetOwner() {
    require(msg.sender == chaosnetOwner, "Not the chaosnet owner");
    _;
  }

  modifier onlyOnChaosnet() {
    require(isChaosnetActive, "Chaosnet is not active");
    _;
  }

  /// @notice Adds beta operator to chaosnet. Can be called only by the
  /// chaosnet owner when the chaosnet is active. Once the operator is added
  /// as a beta operator, it can not be removed.
  function addBetaOperators(address[] calldata operators)
    public
    onlyOnChaosnet
    onlyChaosnetOwner
  {
    for (uint256 i = 0; i < operators.length; i++) {
      isBetaOperator[operators[i]] = true;
    }

    emit BetaOperatorsAdded(operators);
  }

  /// @notice Deactivates the chaosnet. Can be called only by the chaosnet
  /// owner. Once deactivated chaosnet can not be activated again.
  function deactivateChaosnet() public onlyOnChaosnet onlyChaosnetOwner {
    isChaosnetActive = false;
    emit ChaosnetDeactivated();
  }

  /// @notice Transfers the chaosnet owner role to another non-zero address.
  function transferChaosnetOwnerRole(address newChaosnetOwner)
    public
    onlyChaosnetOwner
  {
    require(
      newChaosnetOwner != address(0),
      "New chaosnet owner must not be zero address"
    );
    _transferChaosnetOwner(newChaosnetOwner);
  }

  function _transferChaosnetOwner(address newChaosnetOwner) internal {
    address oldChaosnetOwner = chaosnetOwner;
    chaosnetOwner = newChaosnetOwner;
    emit ChaosnetOwnerRoleTransferred(oldChaosnetOwner, newChaosnetOwner);
  }
}

File 9 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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);
}

File 10 of 16 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 11 of 16 : IApproveAndCall.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

/// @notice An interface that should be implemented by tokens supporting
///         `approveAndCall`/`receiveApproval` pattern.
interface IApproveAndCall {
    /// @notice Executes `receiveApproval` function on spender as specified in
    ///         `IReceiveApproval` interface. Approves spender to withdraw from
    ///         the caller multiple times, up to the `amount`. If this
    ///         function is called again, it overwrites the current allowance
    ///         with `amount`. Reverts if the approval reverted or if
    ///         `receiveApproval` call on the spender reverted.
    function approveAndCall(
        address spender,
        uint256 amount,
        bytes memory extraData
    ) external returns (bool);
}

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

File 13 of 16 : Leaf.sol
pragma solidity 0.8.17;

import "./Constants.sol";

library Leaf {
  function make(
    address _operator,
    uint256 _creationBlock,
    uint256 _id
  ) internal pure returns (uint256) {
    assert(_creationBlock <= type(uint64).max);
    assert(_id <= type(uint32).max);
    // Converting a bytesX type into a larger type
    // adds zero bytes on the right.
    uint256 op = uint256(bytes32(bytes20(_operator)));
    // Bitwise AND the id to erase
    // all but the 32 least significant bits
    uint256 uid = _id & Constants.ID_MAX;
    // Erase all but the 64 least significant bits,
    // then shift left by 32 bits to make room for the id
    uint256 cb = (_creationBlock & Constants.BLOCKHEIGHT_MAX) <<
      Constants.ID_WIDTH;
    // Bitwise OR them all together to get
    // [address operator || uint64 creationBlock || uint32 id]
    return (op | cb | uid);
  }

  function operator(uint256 leaf) internal pure returns (address) {
    // Converting a bytesX type into a smaller type
    // truncates it on the right.
    return address(bytes20(bytes32(leaf)));
  }

  /// @notice Return the block number the leaf was created in.
  function creationBlock(uint256 leaf) internal pure returns (uint256) {
    return ((leaf >> Constants.ID_WIDTH) & Constants.BLOCKHEIGHT_MAX);
  }

  function id(uint256 leaf) internal pure returns (uint32) {
    // Id is stored in the 32 least significant bits.
    // Bitwise AND ensures that we only get the contents of those bits.
    return uint32(leaf & Constants.ID_MAX);
  }
}

File 14 of 16 : Constants.sol
pragma solidity 0.8.17;

library Constants {
  ////////////////////////////////////////////////////////////////////////////
  // Parameters for configuration

  // How many bits a position uses per level of the tree;
  // each branch of the tree contains 2**SLOT_BITS slots.
  uint256 constant SLOT_BITS = 3;
  uint256 constant LEVELS = 7;
  ////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////
  // Derived constants, do not touch
  uint256 constant SLOT_COUNT = 2**SLOT_BITS;
  uint256 constant SLOT_WIDTH = 256 / SLOT_COUNT;
  uint256 constant LAST_SLOT = SLOT_COUNT - 1;
  uint256 constant SLOT_MAX = (2**SLOT_WIDTH) - 1;
  uint256 constant POOL_CAPACITY = SLOT_COUNT**LEVELS;

  uint256 constant ID_WIDTH = SLOT_WIDTH;
  uint256 constant ID_MAX = SLOT_MAX;

  uint256 constant BLOCKHEIGHT_WIDTH = 96 - ID_WIDTH;
  uint256 constant BLOCKHEIGHT_MAX = (2**BLOCKHEIGHT_WIDTH) - 1;

  uint256 constant SLOT_POINTER_MAX = (2**SLOT_BITS) - 1;
  uint256 constant LEAF_FLAG = 1 << 255;

  uint256 constant WEIGHT_WIDTH = 256 / SLOT_COUNT;
  ////////////////////////////////////////////////////////////////////////////
}

File 15 of 16 : Branch.sol
pragma solidity 0.8.17;

import "./Constants.sol";

/// @notice The implicit 8-ary trees of the sortition pool
/// rely on packing 8 "slots" of 32-bit values into each uint256.
/// The Branch library permits efficient calculations on these slots.
library Branch {
  /// @notice Calculate the right shift required
  /// to make the 32 least significant bits of an uint256
  /// be the bits of the `position`th slot
  /// when treating the uint256 as a uint32[8].
  ///
  /// @dev Not used for efficiency reasons,
  /// but left to illustrate the meaning of a common pattern.
  /// I wish solidity had macros, even C macros.
  function slotShift(uint256 position) internal pure returns (uint256) {
    unchecked {
      return position * Constants.SLOT_WIDTH;
    }
  }

  /// @notice Return the `position`th slot of the `node`,
  /// treating `node` as a uint32[32].
  function getSlot(uint256 node, uint256 position)
    internal
    pure
    returns (uint256)
  {
    unchecked {
      uint256 shiftBits = position * Constants.SLOT_WIDTH;
      // Doing a bitwise AND with `SLOT_MAX`
      // clears all but the 32 least significant bits.
      // Because of the right shift by `slotShift(position)` bits,
      // those 32 bits contain the 32 bits in the `position`th slot of `node`.
      return (node >> shiftBits) & Constants.SLOT_MAX;
    }
  }

  /// @notice Return `node` with the `position`th slot set to zero.
  function clearSlot(uint256 node, uint256 position)
    internal
    pure
    returns (uint256)
  {
    unchecked {
      uint256 shiftBits = position * Constants.SLOT_WIDTH;
      // Shifting `SLOT_MAX` left by `slotShift(position)` bits
      // gives us a number where all bits of the `position`th slot are set,
      // and all other bits are unset.
      //
      // Using a bitwise NOT on this number,
      // we get a uint256 where all bits are set
      // except for those of the `position`th slot.
      //
      // Bitwise ANDing the original `node` with this number
      // sets the bits of `position`th slot to zero,
      // leaving all other bits unchanged.
      return node & ~(Constants.SLOT_MAX << shiftBits);
    }
  }

  /// @notice Return `node` with the `position`th slot set to `weight`.
  ///
  /// @param weight The weight of of the node.
  /// Safely truncated to a 32-bit number,
  /// but this should never be called with an overflowing weight regardless.
  function setSlot(
    uint256 node,
    uint256 position,
    uint256 weight
  ) internal pure returns (uint256) {
    unchecked {
      uint256 shiftBits = position * Constants.SLOT_WIDTH;
      // Clear the `position`th slot like in `clearSlot()`.
      uint256 clearedNode = node & ~(Constants.SLOT_MAX << shiftBits);
      // Bitwise AND `weight` with `SLOT_MAX`
      // to clear all but the 32 least significant bits.
      //
      // Shift this left by `slotShift(position)` bits
      // to obtain a uint256 with all bits unset
      // except in the `position`th slot
      // which contains the 32-bit value of `weight`.
      uint256 shiftedWeight = (weight & Constants.SLOT_MAX) << shiftBits;
      // When we bitwise OR these together,
      // all other slots except the `position`th one come from the left argument,
      // and the `position`th gets filled with `weight` from the right argument.
      return clearedNode | shiftedWeight;
    }
  }

  /// @notice Calculate the summed weight of all slots in the `node`.
  function sumWeight(uint256 node) internal pure returns (uint256 sum) {
    unchecked {
      sum = node & Constants.SLOT_MAX;
      // Iterate through each slot
      // by shifting `node` right in increments of 32 bits,
      // and adding the 32 least significant bits to the `sum`.
      uint256 newNode = node >> Constants.SLOT_WIDTH;
      while (newNode > 0) {
        sum += (newNode & Constants.SLOT_MAX);
        newNode = newNode >> Constants.SLOT_WIDTH;
      }
      return sum;
    }
  }

  /// @notice Pick a slot in `node` that corresponds to `index`.
  /// Treats the node like an array of virtual stakers,
  /// the number of virtual stakers in each slot corresponding to its weight,
  /// and picks which slot contains the `index`th virtual staker.
  ///
  /// @dev Requires that `index` be lower than `sumWeight(node)`.
  /// However, this is not enforced for performance reasons.
  /// If `index` exceeds the permitted range,
  /// `pickWeightedSlot()` returns the rightmost slot
  /// and an excessively high `newIndex`.
  ///
  /// @return slot The slot of `node` containing the `index`th virtual staker.
  ///
  /// @return newIndex The index of the `index`th virtual staker of `node`
  /// within the returned slot.
  function pickWeightedSlot(uint256 node, uint256 index)
    internal
    pure
    returns (uint256 slot, uint256 newIndex)
  {
    unchecked {
      newIndex = index;
      uint256 newNode = node;
      uint256 currentSlotWeight = newNode & Constants.SLOT_MAX;
      while (newIndex >= currentSlotWeight) {
        newIndex -= currentSlotWeight;
        slot++;
        newNode = newNode >> Constants.SLOT_WIDTH;
        currentSlotWeight = newNode & Constants.SLOT_MAX;
      }
      return (slot, newIndex);
    }
  }
}

File 16 of 16 : Position.sol
pragma solidity 0.8.17;

import "./Constants.sol";

library Position {
  // Return the last 3 bits of a position number,
  // corresponding to its slot in its parent
  function slot(uint256 a) internal pure returns (uint256) {
    return a & Constants.SLOT_POINTER_MAX;
  }

  // Return the parent of a position number
  function parent(uint256 a) internal pure returns (uint256) {
    return a >> Constants.SLOT_BITS;
  }

  // Return the location of the child of a at the given slot
  function child(uint256 a, uint256 s) internal pure returns (uint256) {
    return (a << Constants.SLOT_BITS) | (s & Constants.SLOT_POINTER_MAX); // slot(s)
  }

  // Return the uint p as a flagged position uint:
  // the least significant 21 bits contain the position
  // and the 22nd bit is set as a flag
  // to distinguish the position 0x000000 from an empty field.
  function setFlag(uint256 p) internal pure returns (uint256) {
    return p | Constants.LEAF_FLAG;
  }

  // Turn a flagged position into an unflagged position
  // by removing the flag at the 22nd least significant bit.
  //
  // We shouldn't _actually_ need this
  // as all position-manipulating code should ignore non-position bits anyway
  // but it's cheap to call so might as well do it.
  function unsetFlag(uint256 p) internal pure returns (uint256) {
    return p & (~Constants.LEAF_FLAG);
  }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC20WithPermit","name":"_rewardToken","type":"address"},{"internalType":"uint256","name":"_poolWeightDivisor","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"operators","type":"address[]"}],"name":"BetaOperatorsAdded","type":"event"},{"anonymous":false,"inputs":[],"name":"ChaosnetDeactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldChaosnetOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newChaosnetOwner","type":"address"}],"name":"ChaosnetOwnerRoleTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32[]","name":"ids","type":"uint32[]"},{"indexed":false,"internalType":"uint256","name":"until","type":"uint256"}],"name":"IneligibleForRewards","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":"operator","type":"address"},{"indexed":true,"internalType":"uint32","name":"id","type":"uint32"}],"name":"RewardEligibilityRestored","type":"event"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"addBetaOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"canRestoreRewardEligibility","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chaosnetOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deactivateChaosnet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getAvailableRewards","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"getIDOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"ids","type":"uint32[]"}],"name":"getIDOperators","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getOperatorID","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getPoolWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ineligibleEarnedRewards","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"authorizedStake","type":"uint256"}],"name":"insertOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isBetaOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isChaosnetActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"isEligibleForRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"isOperatorInPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"isOperatorRegistered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"authorizedStake","type":"uint256"}],"name":"isOperatorUpToDate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"operatorsInPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolWeightDivisor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"receiveApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"restoreRewardEligibility","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract IERC20WithPermit","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"rewardsEligibilityRestorableAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"groupSize","type":"uint256"},{"internalType":"bytes32","name":"seed","type":"bytes32"}],"name":"selectGroup","outputs":[{"internalType":"uint32[]","name":"","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"operators","type":"uint32[]"},{"internalType":"uint256","name":"until","type":"uint256"}],"name":"setRewardIneligibility","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newChaosnetOwner","type":"address"}],"name":"transferChaosnetOwnerRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"authorizedStake","type":"uint256"}],"name":"updateOperatorStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawIneligible","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"withdrawRewards","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"nonpayable","type":"function"}]

60e06040523480156200001157600080fd5b5060405162002c7138038062002c71833981016040819052620000349162000140565b6000808055600481905560078054600101815590524260805262000058336200008d565b6200006333620000df565b600b805460ff60a01b1916600160a01b1790556001600160a01b0390911660a05260c0526200017c565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600d80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527ff7d2871c195d5dcbeca7c9bfb4f7ae4149d0915a5d3d03c8c2286c9a24e932be910160405180910390a15050565b600080604083850312156200015457600080fd5b82516001600160a01b03811681146200016c57600080fd5b6020939093015192949293505050565b60805160a05160c051612a9b620001d6600039600081816102a6015261139001526000818161050101528181610ac801528181610b6701528181610cfb0152610fb10152600081816113040152611aa50152612a9b6000f3fe608060405234801561001057600080fd5b506004361061021b5760003560e01c806396c82e5711610125578063dc7520c5116100ad578063f2fde38b1161007c578063f2fde38b146104e9578063f7186ce014610329578063f7c618c1146104fc578063f7f9a8fa14610523578063f83d08ba1461054357600080fd5b8063dc7520c5146104b3578063e20981ca146104c6578063e7bfd899146104d9578063f23baf4a146104e157600080fd5b8063a9649414116100f4578063a964941414610453578063b0f3828e14610466578063b2f3db4d1461047a578063c0a3f9eb1461048d578063c545b3a9146104a057600080fd5b806396c82e571461041c578063a4e2d63414610424578063a69df4b514610438578063a7a7d3911461044057600080fd5b80636b1906f8116101a8578063873e31fa11610177578063873e31fa146103a75780638871ca5d146103d25780638da5cb5b146103e55780638f4ffcb1146103f6578063942f68921461040957600080fd5b80636b1906f8146103295780636c2530b914610354578063715018a6146103745780637c2cf6cd1461037c57600080fd5b806343a3db30116101ef57806343a3db30146102a15780634de824f0146102c85780635757ed5b146102db5780635a48b46b146102ee578063660186e61461031657600080fd5b8062983b7314610220578063241a418814610246578063398ece9c1461025b5780633e723fc91461028e575b600080fd5b61023361022e3660046123d2565b61054b565b6040519081526020015b60405180910390f35b6102596102543660046123ed565b610569565b005b61027e6102693660046123d2565b600c6020526000908152604090205460ff1681565b604051901515815260200161023d565b61025961029c36600461245c565b6106be565b6102337f000000000000000000000000000000000000000000000000000000000000000081565b61027e6102d63660046123ed565b6107ea565b6102336102e93660046123d2565b610808565b6103016102fc3660046123d2565b610856565b60405163ffffffff909116815260200161023d565b61027e6103243660046123d2565b610877565b61027e6103373660046123d2565b6001600160a01b0316600090815260036020526040902054151590565b61036761036236600461249e565b6108ac565b60405161023d91906124c0565b610259610a4e565b600d5461038f906001600160a01b031681565b6040516001600160a01b03909116815260200161023d565b6103ba6103b53660046123d2565b610a62565b6040516001600160601b03909116815260200161023d565b61038f6103e036600461251e565b610a79565b600b546001600160a01b031661038f565b610259610404366004612539565b610ac6565b6102596104173660046125d4565b610bf0565b610233610c76565b600d5461027e90600160a01b900460ff1681565b610259610c88565b6009546103ba906001600160601b031681565b6102596104613660046123d2565b610c9f565b600b5461027e90600160a01b900460ff1681565b6102596104883660046123d2565b610d6f565b61027e61049b3660046123d2565b610dc5565b6102596104ae3660046123d2565b610ddc565b6102596104c13660046123ed565b610e7c565b6103ba6104d4366004612620565b610f1a565b610233611029565b610259611044565b6102596104f73660046123d2565b6110f8565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b61053661053136600461245c565b61116e565b60405161023d9190612653565b610259611271565b60008061055783610856565b90506105628161128e565b9392505050565b61057161132f565b600d54600160a01b900460ff16156105c85760405162461bcd60e51b815260206004820152601560248201527414dbdc9d1a5d1a5bdb881c1bdbdb081b1bd8dad959605a1b60448201526064015b60405180910390fd5b60006105d382611389565b90506000811161061d5760405162461bcd60e51b81526020600482015260156024820152744f70657261746f72206e6f7420656c696769626c6560581b60448201526064016105bf565b600b54600160a01b900460ff1615610697576001600160a01b0383166000908152600c602052604090205460ff166106975760405162461bcd60e51b815260206004820152601e60248201527f4e6f742062657461206f70657261746f7220666f72206368616f736e6574000060448201526064016105bf565b6106a183826113b5565b60006106ac84610856565b90506106b881836114ab565b50505050565b600b54600160a01b900460ff166107105760405162461bcd60e51b81526020600482015260166024820152754368616f736e6574206973206e6f742061637469766560501b60448201526064016105bf565b600d546001600160a01b0316331461073a5760405162461bcd60e51b81526004016105bf90612694565b60005b818110156107ac576001600c600085858581811061075d5761075d6126c4565b905060200201602081019061077291906123d2565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055806107a4816126f0565b91505061073d565b507f79b60dc9f29a0514f5ce9bf1e89b7add7a22440cde3b203c03a842e3b534071b82826040516107de929190612709565b60405180910390a15050565b60006107f583610808565b6107fe83611389565b1490505b92915050565b6001600160a01b038116600090815260036020526040812054806000036108325750600092915050565b6001600160ff1b038116600061084782611631565b95945050505050565b50919050565b6001600160a01b031660009081526006602052604090205463ffffffff1690565b60008061088383610856565b90506105628163ffffffff9081166000908152600a6020526040902054600160c01b9004161590565b600d54606090600160a01b900460ff166109085760405162461bcd60e51b815260206004820152601760248201527f536f72746974696f6e20706f6f6c20756e6c6f636b656400000000000000000060448201526064016105bf565b600080549083906109188361168e565b90506000811161096a5760405162461bcd60e51b815260206004820152601c60248201527f4e6f7420656e6f756768206f70657261746f727320696e20706f6f6c0000000060448201526064016105bf565b600080610976836116b5565b905060008867ffffffffffffffff81111561099357610993612755565b6040519080825280602002602001820160405280156109bc578160200160208202803683370190505b50905060005b89811015610a41576109d58587856116e7565b9650935060006109e5858961175f565b600081815260026020526040902054909150610a008161182a565b848481518110610a1257610a126126c4565b602002602001019063ffffffff16908163ffffffff168152505050508080610a39906126f0565b9150506109c2565b5098975050505050505050565b610a5661132f565b610a606000611863565b565b600080610a6e83610856565b9050610562816118b5565b60075460009063ffffffff831610610a92576000610802565b60078263ffffffff1681548110610aab57610aab6126c4565b6000918252602090912001546001600160a01b031692915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614610b3b5760405162461bcd60e51b81526020600482015260116024820152702ab739bab83837b93a32b2103a37b5b2b760791b60448201526064016105bf565b6040516323b872dd60e01b81526001600160a01b038681166004830152306024830152604482018690527f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303816000875af1158015610bb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd4919061276b565b50610be984610be460005461168e565b611963565b5050505050565b610bf861132f565b610c36838380806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250859250611a6b915050565b7f01f5838e3dde8cf4817b958fe95be92bdfeccb34317e1d9f58d1cfe5230de231838383604051610c699392919061278d565b60405180910390a1505050565b6000610c8360005461168e565b905090565b610c9061132f565b600d805460ff60a01b19169055565b610ca761132f565b6000610cca600980546001600160601b031981169091556001600160601b031690565b60405163a9059cbb60e01b81526001600160a01b0384811660048301526001600160601b03831660248301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015610d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6a919061276b565b505050565b6000610d7a82610856565b9050610d8581611c6d565b60405163ffffffff8216906001600160a01b038416907fe61e9f0f049b3bfae1ae903a5e3018c02a008aa0d238ffddf23a4fb4c027853690600090a35050565b600080610dd183610856565b905061056281611e09565b600d546001600160a01b03163314610e065760405162461bcd60e51b81526004016105bf90612694565b6001600160a01b038116610e705760405162461bcd60e51b815260206004820152602b60248201527f4e6577206368616f736e6574206f776e6572206d757374206e6f74206265207a60448201526a65726f206164647265737360a81b60648201526084016105bf565b610e7981611e1d565b50565b610e8461132f565b600d54600160a01b900460ff1615610ed65760405162461bcd60e51b815260206004820152601560248201527414dbdc9d1a5d1a5bdb881c1bdbdb081b1bd8dad959605a1b60448201526064016105bf565b6000610ee182611389565b90506000610eee84610856565b9050610efa81836114ab565b81600003610f1057610f0b84611e77565b6106b8565b6106b88483611eeb565b6000610f2461132f565b6000610f2f84610856565b9050610f4381610f3e86610808565b6114ab565b63ffffffff81166000908152600a6020526040812080546bffffffffffffffffffffffff60601b198116909155600160601b90046001600160601b031660405163a9059cbb60e01b81526001600160a01b0386811660048301526001600160601b03831660248301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015610ffc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611020919061276b565b50949350505050565b6004546005546000919061103d81836127de565b9250505090565b600b54600160a01b900460ff166110965760405162461bcd60e51b81526020600482015260166024820152754368616f736e6574206973206e6f742061637469766560501b60448201526064016105bf565b600d546001600160a01b031633146110c05760405162461bcd60e51b81526004016105bf90612694565b600b805460ff60a01b191690556040517fbea11dc6cfde2788be7e8a6ceef5c8d181bb1c628ba6d71675fca0e754367c7490600090a1565b61110061132f565b6001600160a01b0381166111655760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016105bf565b610e7981611863565b60075460609060008367ffffffffffffffff81111561118f5761118f612755565b6040519080825280602002602001820160405280156111b8578160200160208202803683370190505b50905060005b848110156110205760008686838181106111da576111da6126c4565b90506020020160208101906111ef919061251e565b90508063ffffffff168411611205576000611234565b60078163ffffffff168154811061121e5761121e6126c4565b6000918252602090912001546001600160a01b03165b838381518110611246576112466126c4565b6001600160a01b03909216602092830291909101909101525080611269816126f0565b9150506111be565b61127961132f565b600d805460ff60a01b1916600160a01b179055565b63ffffffff8082166000908152600a60205260408120549091600160c01b909104168082036112ff5760405162461bcd60e51b815260206004820152601960248201527f4f70657261746f7220616c726561647920656c696769626c650000000000000060448201526064016105bf565b6105627f000000000000000000000000000000000000000000000000000000000000000063ffffffff83166127f1565b600b546001600160a01b03163314610a605760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016105bf565b60006108027f00000000000000000000000000000000000000000000000000000000000000008361281a565b6001600160a01b0382166000908152600360205260409020541561142e5760405162461bcd60e51b815260206004820152602a60248201527f4f70657261746f7220697320616c7265616479207265676973746572656420696044820152691b881d1a19481c1bdbdb60b21b60648201526084016105bf565b600061143983610856565b63ffffffff169050806000036114555761145283611f5c565b90505b600061145f61201f565b9050600061146e854385612102565b905061147e8282866000546121db565b600055600160ff1b82176001600160a01b0390951660009081526003602052604090209490945550505050565b60085463ffffffff8084166000908152600a60209081526040808320815160808101835290546001600160601b03818116808452600160601b8304821695840195909552600160c01b8204871693830193909352600160e01b9004909416606085018190529416939061151e908561282e565b6115289190612855565b9050816040015163ffffffff1660000361155f57808260200181815161154e9190612880565b6001600160601b03169052506115a2565b6009805482919060009061157d9084906001600160601b0316612880565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b506001600160601b03918216815263ffffffff928316606082019081529383166000908152600a60209081526040918290208351815492850151939094015196518616600160e01b026001600160e01b0397909616600160c01b02969096166001600160c01b03928516600160601b026001600160c01b03199092169390941692909217919091171617179055565b60008061163d836121f7565b9050600061164b8460031c90565b60008181527fdc686ec4a0ff239c70e7c7c36e8f853eced3bc8618f48d2b816da2a74311237e602052604090205490915061084781846020021c63ffffffff1690565b63ffffffff8116602082901c5b80156108505763ffffffff8116919091019060201c61169b565b6000816001036116c757506000919050565b601f5b82816001901b106116de57600019016116ca565b60010192915050565b6000808080855b82611752576000196001871b0181169150803060405160200161172892919091825260601b6001600160601b031916602082015260340190565b6040516020818303038152906040528051906020012090508782101561174d57600192505b6116ee565b9097909650945050505050565b60008282828061176e8361168e565b87106117b35760405162461bcd60e51b8152602060048201526014602482015273125b99195e08195e18d959591cc81dd95a59da1d60621b60448201526064016105bf565b6117bd8385612207565b9450905060025b60078111611814576117d6838361223e565b6000828152600160209081526040808320848452909152902054945092506117fe8486612207565b955091508061180c816126f0565b9150506117c4565b5061181f828261223e565b979650505050505050565b6000600161183a60036002612984565b6118469061010061281a565b611851906002612984565b61185b91906127de565b909116919050565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60085463ffffffff8083166000908152600a60209081526040808320815160808101835290546001600160601b038181168352600160601b8204811694830194909452600160c01b81048616928201839052600160e01b9004909416606085015291931691908303611958576060810151815160009163ffffffff169061193c908561282e565b6119469190612855565b90508082602001516108479190612880565b602001519392505050565b60008163ffffffff16116119b15760405162461bcd60e51b8152602060048201526015602482015274139bc81c9958da5c1a595b9d1cc81a5b881c1bdbdb605a1b60448201526064016105bf565b6008546000906119d190600160601b90046001600160601b031684612880565b905060006119e563ffffffff841683612990565b905060006119f963ffffffff8516846129b6565b600880549192508391600090611a199084906001600160601b0316612880565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550806008600c6101000a8154816001600160601b0302191690836001600160601b031602179055505050505050565b604080516080810182526000808252602082018190529181018290526060810182905260085490916001600160601b039091169080611aca7f0000000000000000000000000000000000000000000000000000000000000000866127de565b905060005b8651811015611c64576000878281518110611aec57611aec6126c4565b60209081029190910181015163ffffffff8082166000908152600a845260409081902080546001600160601b03600160601b82048116968d01969096529485168b52600160c01b85048316918b01829052600160e01b90940490911660608a015290925015611b7f578363ffffffff16876040015163ffffffff161015611b7a5763ffffffff841660408801525b611bcb565b63ffffffff808516604089015260608801518851911690611ba0908861282e565b611baa9190612855565b94508487602001818151611bbe9190612880565b6001600160601b03169052505b6001600160601b038681168089526020890151835460408b015160608c01516001600160e01b0319909216600160601b93909516929092027fffffffff00000000ffffffffffffffffffffffff0000000000000000000000001693909317909117600160c01b63ffffffff92831602176001600160e01b0316600160e01b91909216021790555080611c5c816126f0565b915050611acf565b50505050505050565b611c7681611e09565b611cc25760405162461bcd60e51b815260206004820152601960248201527f4f70657261746f72207374696c6c20696e656c696769626c650000000000000060448201526064016105bf565b60085463ffffffff8083166000908152600a60209081526040808320815160808101835290546001600160601b03818116808452600160601b8304821695840195909552600160c01b8204871693830193909352600160e01b90049094166060850181905294169390611d35908561282e565b611d3f9190612855565b600980549192508291600090611d5f9084906001600160601b0316612880565b82546001600160601b039182166101009390930a928302928202191691909117909155938416835250506000604080830182815263ffffffff9586168352600a602090815291909220835181549285015193516060909501518716600160e01b026001600160e01b0395909716600160c01b02949094166001600160c01b03938616600160601b026001600160c01b03199093169490951693909317171691909117919091179055565b600042611e158361128e565b111592915050565b600d80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527ff7d2871c195d5dcbeca7c9bfb4f7ae4149d0915a5d3d03c8c2286c9a24e932be91016107de565b6001600160a01b03811660009081526003602052604081205490819003611eb05760405162461bcd60e51b81526004016105bf906129dc565b60006001600160ff1b0382169050611eca81600054612268565b600055610d6a836001600160a01b0316600090815260036020526040812055565b6001600160a01b038216600090815260036020526040902054611f205760405162461bcd60e51b81526004016105bf906129dc565b6001600160a01b03821660009081526003602052604081205490546001600160ff1b03821690611f5390829085906122dd565b60005550505050565b60075460009063ffffffff811115611faf5760405162461bcd60e51b8152602060048201526016602482015275141bdbdb0818d85c1858da5d1e48195e18d95959195960521b60448201526064016105bf565b6001600160a01b03929092166000818152600660205260408120805463ffffffff191663ffffffff86161790556007805460018101825591527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180546001600160a01b03191690911790555090565b60045460009081600761203460036002612984565b61203e9190612984565b6120498360016127f1565b10905080156120665761205d8260016127f1565b60045550919050565b600554806120a55760405162461bcd60e51b815260206004820152600c60248201526b141bdbdb081a5cc8199d5b1b60a21b60448201526064016105bf565b600060056120b46001846127de565b815481106120c4576120c46126c4565b9060005260206000200154905060058054806120e2576120e2612a22565b600190038181906000526020600020016000905590558094505050505090565b600067ffffffffffffffff83111561211c5761211c612a38565b63ffffffff82111561213057612130612a38565b6001600160601b0319606085901b166000600161214f60036002612984565b61215b9061010061281a565b612166906002612984565b61217091906127de565b84169050600061218260036002612984565b61218e9061010061281a565b600161219c60036002612984565b6121a89061010061281a565b6121b39060606127de565b6121be906002612984565b6121c891906127de565b8716901b92909217179150509392505050565b6000848152600260205260408120849055610847858484612307565b6000600161185160036002612984565b6000818363ffffffff81165b80831061223457600193909301929091039060201c63ffffffff8116612213565b50505b9250929050565b6000600161224e60036002612984565b61225891906127de565b8216600384901b17905092915050565b600080600160045461227a91906127de565b9050838114600061228d868280886121db565b9050811561229f576004839055610847565b600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00186905595945050505050565b6000826122e985611631565b14612300576122f9848484612307565b9050610562565b5080610562565b6000808080858760075b6002811061238857612322826121f7565b955061232e8260031c90565b60008281526001602090815260408083208484528252909120805463ffffffff928a0283811b198216938816901b9290921790819055909650945091506123748461168e565b92508061238081612a4e565b915050612311565b50612392816121f7565b945063ffffffff6020860281811b198916918416901b179998505050505050505050565b80356001600160a01b03811681146123cd57600080fd5b919050565b6000602082840312156123e457600080fd5b610562826123b6565b6000806040838503121561240057600080fd5b612409836123b6565b946020939093013593505050565b60008083601f84011261242957600080fd5b50813567ffffffffffffffff81111561244157600080fd5b6020830191508360208260051b850101111561223757600080fd5b6000806020838503121561246f57600080fd5b823567ffffffffffffffff81111561248657600080fd5b61249285828601612417565b90969095509350505050565b600080604083850312156124b157600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b818110156124fe57835163ffffffff16835292840192918401916001016124dc565b50909695505050505050565b803563ffffffff811681146123cd57600080fd5b60006020828403121561253057600080fd5b6105628261250a565b60008060008060006080868803121561255157600080fd5b61255a866123b6565b94506020860135935061256f604087016123b6565b9250606086013567ffffffffffffffff8082111561258c57600080fd5b818801915088601f8301126125a057600080fd5b8135818111156125af57600080fd5b8960208285010111156125c157600080fd5b9699959850939650602001949392505050565b6000806000604084860312156125e957600080fd5b833567ffffffffffffffff81111561260057600080fd5b61260c86828701612417565b909790965060209590950135949350505050565b6000806040838503121561263357600080fd5b61263c836123b6565b915061264a602084016123b6565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156124fe5783516001600160a01b03168352928401929184019160010161266f565b6020808252601690820152752737ba103a34329031b430b7b9b732ba1037bbb732b960511b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201612702576127026126da565b5060010190565b60208082528181018390526000908460408401835b8681101561274a576001600160a01b03612737846123b6565b168252918301919083019060010161271e565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561277d57600080fd5b8151801515811461056257600080fd5b6040808252810183905260008460608301825b868110156127cb5763ffffffff6127b68461250a565b168252602092830192909101906001016127a0565b5060209390930193909352509392505050565b81810381811115610802576108026126da565b80820180821115610802576108026126da565b634e487b7160e01b600052601260045260246000fd5b60008261282957612829612804565b500490565b6001600160601b0382811682821603908082111561284e5761284e6126da565b5092915050565b6001600160601b03818116838216028082169190828114612878576128786126da565b505092915050565b6001600160601b0381811683821601908082111561284e5761284e6126da565b600181815b808511156128db5781600019048211156128c1576128c16126da565b808516156128ce57918102915b93841c93908002906128a5565b509250929050565b6000826128f257506001610802565b816128ff57506000610802565b8160018114612915576002811461291f5761293b565b6001915050610802565b60ff841115612930576129306126da565b50506001821b610802565b5060208310610133831016604e8410600b841016171561295e575081810a610802565b61296883836128a0565b806000190482111561297c5761297c6126da565b029392505050565b600061056283836128e3565b60006001600160601b03808416806129aa576129aa612804565b92169190910492915050565b60006001600160601b03808416806129d0576129d0612804565b92169190910692915050565b60208082526026908201527f4f70657261746f72206973206e6f74207265676973746572656420696e20746860408201526519481c1bdbdb60d21b606082015260800190565b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b600081612a5d57612a5d6126da565b50600019019056fea26469706673582212200777efb6bbaffaf3c12f9366651c0738fd0510bf19a96c3af0a337226cca78d364736f6c63430008110033000000000000000000000000cdf7028ceab81fa0c6971208e83fa7872994bee50000000000000000000000000000000000000000000000000de0b6b3a7640000

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061021b5760003560e01c806396c82e5711610125578063dc7520c5116100ad578063f2fde38b1161007c578063f2fde38b146104e9578063f7186ce014610329578063f7c618c1146104fc578063f7f9a8fa14610523578063f83d08ba1461054357600080fd5b8063dc7520c5146104b3578063e20981ca146104c6578063e7bfd899146104d9578063f23baf4a146104e157600080fd5b8063a9649414116100f4578063a964941414610453578063b0f3828e14610466578063b2f3db4d1461047a578063c0a3f9eb1461048d578063c545b3a9146104a057600080fd5b806396c82e571461041c578063a4e2d63414610424578063a69df4b514610438578063a7a7d3911461044057600080fd5b80636b1906f8116101a8578063873e31fa11610177578063873e31fa146103a75780638871ca5d146103d25780638da5cb5b146103e55780638f4ffcb1146103f6578063942f68921461040957600080fd5b80636b1906f8146103295780636c2530b914610354578063715018a6146103745780637c2cf6cd1461037c57600080fd5b806343a3db30116101ef57806343a3db30146102a15780634de824f0146102c85780635757ed5b146102db5780635a48b46b146102ee578063660186e61461031657600080fd5b8062983b7314610220578063241a418814610246578063398ece9c1461025b5780633e723fc91461028e575b600080fd5b61023361022e3660046123d2565b61054b565b6040519081526020015b60405180910390f35b6102596102543660046123ed565b610569565b005b61027e6102693660046123d2565b600c6020526000908152604090205460ff1681565b604051901515815260200161023d565b61025961029c36600461245c565b6106be565b6102337f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b61027e6102d63660046123ed565b6107ea565b6102336102e93660046123d2565b610808565b6103016102fc3660046123d2565b610856565b60405163ffffffff909116815260200161023d565b61027e6103243660046123d2565b610877565b61027e6103373660046123d2565b6001600160a01b0316600090815260036020526040902054151590565b61036761036236600461249e565b6108ac565b60405161023d91906124c0565b610259610a4e565b600d5461038f906001600160a01b031681565b6040516001600160a01b03909116815260200161023d565b6103ba6103b53660046123d2565b610a62565b6040516001600160601b03909116815260200161023d565b61038f6103e036600461251e565b610a79565b600b546001600160a01b031661038f565b610259610404366004612539565b610ac6565b6102596104173660046125d4565b610bf0565b610233610c76565b600d5461027e90600160a01b900460ff1681565b610259610c88565b6009546103ba906001600160601b031681565b6102596104613660046123d2565b610c9f565b600b5461027e90600160a01b900460ff1681565b6102596104883660046123d2565b610d6f565b61027e61049b3660046123d2565b610dc5565b6102596104ae3660046123d2565b610ddc565b6102596104c13660046123ed565b610e7c565b6103ba6104d4366004612620565b610f1a565b610233611029565b610259611044565b6102596104f73660046123d2565b6110f8565b61038f7f000000000000000000000000cdf7028ceab81fa0c6971208e83fa7872994bee581565b61053661053136600461245c565b61116e565b60405161023d9190612653565b610259611271565b60008061055783610856565b90506105628161128e565b9392505050565b61057161132f565b600d54600160a01b900460ff16156105c85760405162461bcd60e51b815260206004820152601560248201527414dbdc9d1a5d1a5bdb881c1bdbdb081b1bd8dad959605a1b60448201526064015b60405180910390fd5b60006105d382611389565b90506000811161061d5760405162461bcd60e51b81526020600482015260156024820152744f70657261746f72206e6f7420656c696769626c6560581b60448201526064016105bf565b600b54600160a01b900460ff1615610697576001600160a01b0383166000908152600c602052604090205460ff166106975760405162461bcd60e51b815260206004820152601e60248201527f4e6f742062657461206f70657261746f7220666f72206368616f736e6574000060448201526064016105bf565b6106a183826113b5565b60006106ac84610856565b90506106b881836114ab565b50505050565b600b54600160a01b900460ff166107105760405162461bcd60e51b81526020600482015260166024820152754368616f736e6574206973206e6f742061637469766560501b60448201526064016105bf565b600d546001600160a01b0316331461073a5760405162461bcd60e51b81526004016105bf90612694565b60005b818110156107ac576001600c600085858581811061075d5761075d6126c4565b905060200201602081019061077291906123d2565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055806107a4816126f0565b91505061073d565b507f79b60dc9f29a0514f5ce9bf1e89b7add7a22440cde3b203c03a842e3b534071b82826040516107de929190612709565b60405180910390a15050565b60006107f583610808565b6107fe83611389565b1490505b92915050565b6001600160a01b038116600090815260036020526040812054806000036108325750600092915050565b6001600160ff1b038116600061084782611631565b95945050505050565b50919050565b6001600160a01b031660009081526006602052604090205463ffffffff1690565b60008061088383610856565b90506105628163ffffffff9081166000908152600a6020526040902054600160c01b9004161590565b600d54606090600160a01b900460ff166109085760405162461bcd60e51b815260206004820152601760248201527f536f72746974696f6e20706f6f6c20756e6c6f636b656400000000000000000060448201526064016105bf565b600080549083906109188361168e565b90506000811161096a5760405162461bcd60e51b815260206004820152601c60248201527f4e6f7420656e6f756768206f70657261746f727320696e20706f6f6c0000000060448201526064016105bf565b600080610976836116b5565b905060008867ffffffffffffffff81111561099357610993612755565b6040519080825280602002602001820160405280156109bc578160200160208202803683370190505b50905060005b89811015610a41576109d58587856116e7565b9650935060006109e5858961175f565b600081815260026020526040902054909150610a008161182a565b848481518110610a1257610a126126c4565b602002602001019063ffffffff16908163ffffffff168152505050508080610a39906126f0565b9150506109c2565b5098975050505050505050565b610a5661132f565b610a606000611863565b565b600080610a6e83610856565b9050610562816118b5565b60075460009063ffffffff831610610a92576000610802565b60078263ffffffff1681548110610aab57610aab6126c4565b6000918252602090912001546001600160a01b031692915050565b7f000000000000000000000000cdf7028ceab81fa0c6971208e83fa7872994bee56001600160a01b0316836001600160a01b031614610b3b5760405162461bcd60e51b81526020600482015260116024820152702ab739bab83837b93a32b2103a37b5b2b760791b60448201526064016105bf565b6040516323b872dd60e01b81526001600160a01b038681166004830152306024830152604482018690527f000000000000000000000000cdf7028ceab81fa0c6971208e83fa7872994bee516906323b872dd906064016020604051808303816000875af1158015610bb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd4919061276b565b50610be984610be460005461168e565b611963565b5050505050565b610bf861132f565b610c36838380806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250859250611a6b915050565b7f01f5838e3dde8cf4817b958fe95be92bdfeccb34317e1d9f58d1cfe5230de231838383604051610c699392919061278d565b60405180910390a1505050565b6000610c8360005461168e565b905090565b610c9061132f565b600d805460ff60a01b19169055565b610ca761132f565b6000610cca600980546001600160601b031981169091556001600160601b031690565b60405163a9059cbb60e01b81526001600160a01b0384811660048301526001600160601b03831660248301529192507f000000000000000000000000cdf7028ceab81fa0c6971208e83fa7872994bee59091169063a9059cbb906044016020604051808303816000875af1158015610d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6a919061276b565b505050565b6000610d7a82610856565b9050610d8581611c6d565b60405163ffffffff8216906001600160a01b038416907fe61e9f0f049b3bfae1ae903a5e3018c02a008aa0d238ffddf23a4fb4c027853690600090a35050565b600080610dd183610856565b905061056281611e09565b600d546001600160a01b03163314610e065760405162461bcd60e51b81526004016105bf90612694565b6001600160a01b038116610e705760405162461bcd60e51b815260206004820152602b60248201527f4e6577206368616f736e6574206f776e6572206d757374206e6f74206265207a60448201526a65726f206164647265737360a81b60648201526084016105bf565b610e7981611e1d565b50565b610e8461132f565b600d54600160a01b900460ff1615610ed65760405162461bcd60e51b815260206004820152601560248201527414dbdc9d1a5d1a5bdb881c1bdbdb081b1bd8dad959605a1b60448201526064016105bf565b6000610ee182611389565b90506000610eee84610856565b9050610efa81836114ab565b81600003610f1057610f0b84611e77565b6106b8565b6106b88483611eeb565b6000610f2461132f565b6000610f2f84610856565b9050610f4381610f3e86610808565b6114ab565b63ffffffff81166000908152600a6020526040812080546bffffffffffffffffffffffff60601b198116909155600160601b90046001600160601b031660405163a9059cbb60e01b81526001600160a01b0386811660048301526001600160601b03831660248301529192507f000000000000000000000000cdf7028ceab81fa0c6971208e83fa7872994bee59091169063a9059cbb906044016020604051808303816000875af1158015610ffc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611020919061276b565b50949350505050565b6004546005546000919061103d81836127de565b9250505090565b600b54600160a01b900460ff166110965760405162461bcd60e51b81526020600482015260166024820152754368616f736e6574206973206e6f742061637469766560501b60448201526064016105bf565b600d546001600160a01b031633146110c05760405162461bcd60e51b81526004016105bf90612694565b600b805460ff60a01b191690556040517fbea11dc6cfde2788be7e8a6ceef5c8d181bb1c628ba6d71675fca0e754367c7490600090a1565b61110061132f565b6001600160a01b0381166111655760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016105bf565b610e7981611863565b60075460609060008367ffffffffffffffff81111561118f5761118f612755565b6040519080825280602002602001820160405280156111b8578160200160208202803683370190505b50905060005b848110156110205760008686838181106111da576111da6126c4565b90506020020160208101906111ef919061251e565b90508063ffffffff168411611205576000611234565b60078163ffffffff168154811061121e5761121e6126c4565b6000918252602090912001546001600160a01b03165b838381518110611246576112466126c4565b6001600160a01b03909216602092830291909101909101525080611269816126f0565b9150506111be565b61127961132f565b600d805460ff60a01b1916600160a01b179055565b63ffffffff8082166000908152600a60205260408120549091600160c01b909104168082036112ff5760405162461bcd60e51b815260206004820152601960248201527f4f70657261746f7220616c726561647920656c696769626c650000000000000060448201526064016105bf565b6105627f000000000000000000000000000000000000000000000000000000006335a5ff63ffffffff83166127f1565b600b546001600160a01b03163314610a605760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016105bf565b60006108027f0000000000000000000000000000000000000000000000000de0b6b3a76400008361281a565b6001600160a01b0382166000908152600360205260409020541561142e5760405162461bcd60e51b815260206004820152602a60248201527f4f70657261746f7220697320616c7265616479207265676973746572656420696044820152691b881d1a19481c1bdbdb60b21b60648201526084016105bf565b600061143983610856565b63ffffffff169050806000036114555761145283611f5c565b90505b600061145f61201f565b9050600061146e854385612102565b905061147e8282866000546121db565b600055600160ff1b82176001600160a01b0390951660009081526003602052604090209490945550505050565b60085463ffffffff8084166000908152600a60209081526040808320815160808101835290546001600160601b03818116808452600160601b8304821695840195909552600160c01b8204871693830193909352600160e01b9004909416606085018190529416939061151e908561282e565b6115289190612855565b9050816040015163ffffffff1660000361155f57808260200181815161154e9190612880565b6001600160601b03169052506115a2565b6009805482919060009061157d9084906001600160601b0316612880565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b506001600160601b03918216815263ffffffff928316606082019081529383166000908152600a60209081526040918290208351815492850151939094015196518616600160e01b026001600160e01b0397909616600160c01b02969096166001600160c01b03928516600160601b026001600160c01b03199092169390941692909217919091171617179055565b60008061163d836121f7565b9050600061164b8460031c90565b60008181527fdc686ec4a0ff239c70e7c7c36e8f853eced3bc8618f48d2b816da2a74311237e602052604090205490915061084781846020021c63ffffffff1690565b63ffffffff8116602082901c5b80156108505763ffffffff8116919091019060201c61169b565b6000816001036116c757506000919050565b601f5b82816001901b106116de57600019016116ca565b60010192915050565b6000808080855b82611752576000196001871b0181169150803060405160200161172892919091825260601b6001600160601b031916602082015260340190565b6040516020818303038152906040528051906020012090508782101561174d57600192505b6116ee565b9097909650945050505050565b60008282828061176e8361168e565b87106117b35760405162461bcd60e51b8152602060048201526014602482015273125b99195e08195e18d959591cc81dd95a59da1d60621b60448201526064016105bf565b6117bd8385612207565b9450905060025b60078111611814576117d6838361223e565b6000828152600160209081526040808320848452909152902054945092506117fe8486612207565b955091508061180c816126f0565b9150506117c4565b5061181f828261223e565b979650505050505050565b6000600161183a60036002612984565b6118469061010061281a565b611851906002612984565b61185b91906127de565b909116919050565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60085463ffffffff8083166000908152600a60209081526040808320815160808101835290546001600160601b038181168352600160601b8204811694830194909452600160c01b81048616928201839052600160e01b9004909416606085015291931691908303611958576060810151815160009163ffffffff169061193c908561282e565b6119469190612855565b90508082602001516108479190612880565b602001519392505050565b60008163ffffffff16116119b15760405162461bcd60e51b8152602060048201526015602482015274139bc81c9958da5c1a595b9d1cc81a5b881c1bdbdb605a1b60448201526064016105bf565b6008546000906119d190600160601b90046001600160601b031684612880565b905060006119e563ffffffff841683612990565b905060006119f963ffffffff8516846129b6565b600880549192508391600090611a199084906001600160601b0316612880565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550806008600c6101000a8154816001600160601b0302191690836001600160601b031602179055505050505050565b604080516080810182526000808252602082018190529181018290526060810182905260085490916001600160601b039091169080611aca7f000000000000000000000000000000000000000000000000000000006335a5ff866127de565b905060005b8651811015611c64576000878281518110611aec57611aec6126c4565b60209081029190910181015163ffffffff8082166000908152600a845260409081902080546001600160601b03600160601b82048116968d01969096529485168b52600160c01b85048316918b01829052600160e01b90940490911660608a015290925015611b7f578363ffffffff16876040015163ffffffff161015611b7a5763ffffffff841660408801525b611bcb565b63ffffffff808516604089015260608801518851911690611ba0908861282e565b611baa9190612855565b94508487602001818151611bbe9190612880565b6001600160601b03169052505b6001600160601b038681168089526020890151835460408b015160608c01516001600160e01b0319909216600160601b93909516929092027fffffffff00000000ffffffffffffffffffffffff0000000000000000000000001693909317909117600160c01b63ffffffff92831602176001600160e01b0316600160e01b91909216021790555080611c5c816126f0565b915050611acf565b50505050505050565b611c7681611e09565b611cc25760405162461bcd60e51b815260206004820152601960248201527f4f70657261746f72207374696c6c20696e656c696769626c650000000000000060448201526064016105bf565b60085463ffffffff8083166000908152600a60209081526040808320815160808101835290546001600160601b03818116808452600160601b8304821695840195909552600160c01b8204871693830193909352600160e01b90049094166060850181905294169390611d35908561282e565b611d3f9190612855565b600980549192508291600090611d5f9084906001600160601b0316612880565b82546001600160601b039182166101009390930a928302928202191691909117909155938416835250506000604080830182815263ffffffff9586168352600a602090815291909220835181549285015193516060909501518716600160e01b026001600160e01b0395909716600160c01b02949094166001600160c01b03938616600160601b026001600160c01b03199093169490951693909317171691909117919091179055565b600042611e158361128e565b111592915050565b600d80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527ff7d2871c195d5dcbeca7c9bfb4f7ae4149d0915a5d3d03c8c2286c9a24e932be91016107de565b6001600160a01b03811660009081526003602052604081205490819003611eb05760405162461bcd60e51b81526004016105bf906129dc565b60006001600160ff1b0382169050611eca81600054612268565b600055610d6a836001600160a01b0316600090815260036020526040812055565b6001600160a01b038216600090815260036020526040902054611f205760405162461bcd60e51b81526004016105bf906129dc565b6001600160a01b03821660009081526003602052604081205490546001600160ff1b03821690611f5390829085906122dd565b60005550505050565b60075460009063ffffffff811115611faf5760405162461bcd60e51b8152602060048201526016602482015275141bdbdb0818d85c1858da5d1e48195e18d95959195960521b60448201526064016105bf565b6001600160a01b03929092166000818152600660205260408120805463ffffffff191663ffffffff86161790556007805460018101825591527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180546001600160a01b03191690911790555090565b60045460009081600761203460036002612984565b61203e9190612984565b6120498360016127f1565b10905080156120665761205d8260016127f1565b60045550919050565b600554806120a55760405162461bcd60e51b815260206004820152600c60248201526b141bdbdb081a5cc8199d5b1b60a21b60448201526064016105bf565b600060056120b46001846127de565b815481106120c4576120c46126c4565b9060005260206000200154905060058054806120e2576120e2612a22565b600190038181906000526020600020016000905590558094505050505090565b600067ffffffffffffffff83111561211c5761211c612a38565b63ffffffff82111561213057612130612a38565b6001600160601b0319606085901b166000600161214f60036002612984565b61215b9061010061281a565b612166906002612984565b61217091906127de565b84169050600061218260036002612984565b61218e9061010061281a565b600161219c60036002612984565b6121a89061010061281a565b6121b39060606127de565b6121be906002612984565b6121c891906127de565b8716901b92909217179150509392505050565b6000848152600260205260408120849055610847858484612307565b6000600161185160036002612984565b6000818363ffffffff81165b80831061223457600193909301929091039060201c63ffffffff8116612213565b50505b9250929050565b6000600161224e60036002612984565b61225891906127de565b8216600384901b17905092915050565b600080600160045461227a91906127de565b9050838114600061228d868280886121db565b9050811561229f576004839055610847565b600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00186905595945050505050565b6000826122e985611631565b14612300576122f9848484612307565b9050610562565b5080610562565b6000808080858760075b6002811061238857612322826121f7565b955061232e8260031c90565b60008281526001602090815260408083208484528252909120805463ffffffff928a0283811b198216938816901b9290921790819055909650945091506123748461168e565b92508061238081612a4e565b915050612311565b50612392816121f7565b945063ffffffff6020860281811b198916918416901b179998505050505050505050565b80356001600160a01b03811681146123cd57600080fd5b919050565b6000602082840312156123e457600080fd5b610562826123b6565b6000806040838503121561240057600080fd5b612409836123b6565b946020939093013593505050565b60008083601f84011261242957600080fd5b50813567ffffffffffffffff81111561244157600080fd5b6020830191508360208260051b850101111561223757600080fd5b6000806020838503121561246f57600080fd5b823567ffffffffffffffff81111561248657600080fd5b61249285828601612417565b90969095509350505050565b600080604083850312156124b157600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b818110156124fe57835163ffffffff16835292840192918401916001016124dc565b50909695505050505050565b803563ffffffff811681146123cd57600080fd5b60006020828403121561253057600080fd5b6105628261250a565b60008060008060006080868803121561255157600080fd5b61255a866123b6565b94506020860135935061256f604087016123b6565b9250606086013567ffffffffffffffff8082111561258c57600080fd5b818801915088601f8301126125a057600080fd5b8135818111156125af57600080fd5b8960208285010111156125c157600080fd5b9699959850939650602001949392505050565b6000806000604084860312156125e957600080fd5b833567ffffffffffffffff81111561260057600080fd5b61260c86828701612417565b909790965060209590950135949350505050565b6000806040838503121561263357600080fd5b61263c836123b6565b915061264a602084016123b6565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156124fe5783516001600160a01b03168352928401929184019160010161266f565b6020808252601690820152752737ba103a34329031b430b7b9b732ba1037bbb732b960511b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201612702576127026126da565b5060010190565b60208082528181018390526000908460408401835b8681101561274a576001600160a01b03612737846123b6565b168252918301919083019060010161271e565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561277d57600080fd5b8151801515811461056257600080fd5b6040808252810183905260008460608301825b868110156127cb5763ffffffff6127b68461250a565b168252602092830192909101906001016127a0565b5060209390930193909352509392505050565b81810381811115610802576108026126da565b80820180821115610802576108026126da565b634e487b7160e01b600052601260045260246000fd5b60008261282957612829612804565b500490565b6001600160601b0382811682821603908082111561284e5761284e6126da565b5092915050565b6001600160601b03818116838216028082169190828114612878576128786126da565b505092915050565b6001600160601b0381811683821601908082111561284e5761284e6126da565b600181815b808511156128db5781600019048211156128c1576128c16126da565b808516156128ce57918102915b93841c93908002906128a5565b509250929050565b6000826128f257506001610802565b816128ff57506000610802565b8160018114612915576002811461291f5761293b565b6001915050610802565b60ff841115612930576129306126da565b50506001821b610802565b5060208310610133831016604e8410600b841016171561295e575081810a610802565b61296883836128a0565b806000190482111561297c5761297c6126da565b029392505050565b600061056283836128e3565b60006001600160601b03808416806129aa576129aa612804565b92169190910492915050565b60006001600160601b03808416806129d0576129d0612804565b92169190910692915050565b60208082526026908201527f4f70657261746f72206973206e6f74207265676973746572656420696e20746860408201526519481c1bdbdb60d21b606082015260800190565b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b600081612a5d57612a5d6126da565b50600019019056fea26469706673582212200777efb6bbaffaf3c12f9366651c0738fd0510bf19a96c3af0a337226cca78d364736f6c63430008110033

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

000000000000000000000000cdf7028ceab81fa0c6971208e83fa7872994bee50000000000000000000000000000000000000000000000000de0b6b3a7640000

-----Decoded View---------------
Arg [0] : _rewardToken (address): 0xCdF7028ceAB81fA0C6971208e83fa7872994beE5
Arg [1] : _poolWeightDivisor (uint256): 1000000000000000000

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000cdf7028ceab81fa0c6971208e83fa7872994bee5
Arg [1] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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