ETH Price: $2,718.58 (+0.32%)

Contract

0x3feB1e09b4bb0E7f0387CeE092a52e85797ab889
 
Transaction Hash
Method
Block
From
To
0x05a94ad4fc1dc16b5f9b8a2d74cf4cbe9cbcf8a606e72a85368b4d40469d5f96 Migrate(pending)2025-02-16 6:27:418 mins ago1739687261IN
Chainlink: Staking
0 ETH(Pending)(Pending)
Migrate218403372025-02-13 21:47:592 days ago1739483279IN
Chainlink: Staking
0 ETH0.000371873.21161989
Migrate218235892025-02-11 13:30:114 days ago1739280611IN
Chainlink: Staking
0 ETH0.000240561.81060251
Migrate218223992025-02-11 9:30:474 days ago1739266247IN
Chainlink: Staking
0 ETH0.000298291.36663147
Migrate218116982025-02-09 21:39:356 days ago1739137175IN
Chainlink: Staking
0 ETH0.000228531.7198931
Migrate218095442025-02-09 14:26:116 days ago1739111171IN
Chainlink: Staking
0 ETH0.00016471.23964049
Migrate217860902025-02-06 7:52:599 days ago1738828379IN
Chainlink: Staking
0 ETH0.000211111.58864701
Migrate217195472025-01-28 0:49:1119 days ago1738025351IN
Chainlink: Staking
0 ETH0.000476924.11882659
Migrate217133162025-01-27 3:57:2320 days ago1737950243IN
Chainlink: Staking
0 ETH0.000682165.89191652
Migrate216724882025-01-21 11:12:3525 days ago1737457955IN
Chainlink: Staking
0 ETH0.0012155410.49884448
Migrate216698952025-01-21 2:31:3526 days ago1737426695IN
Chainlink: Staking
0 ETH0.0018509215.98503739
Migrate216667582025-01-20 16:00:1126 days ago1737388811IN
Chainlink: Staking
0 ETH0.0050220643.37632014
Migrate216613352025-01-19 21:51:3527 days ago1737323495IN
Chainlink: Staking
0 ETH0.01231647106.37917353
Migrate216414552025-01-17 3:13:5930 days ago1737083639IN
Chainlink: Staking
0 ETH0.000575394.96979701
Migrate216390542025-01-16 19:11:3530 days ago1737054695IN
Chainlink: Staking
0 ETH0.001506711.33791016
Migrate215683612025-01-06 22:19:1140 days ago1736201951IN
Chainlink: Staking
0 ETH0.0018215215.73278322
Migrate215682082025-01-06 21:48:3540 days ago1736200115IN
Chainlink: Staking
0 ETH0.0025818419.4300593
Migrate215609802025-01-05 21:31:3541 days ago1736112695IN
Chainlink: Staking
0 ETH0.000915897.91067466
Migrate215230602024-12-31 14:30:3546 days ago1735655435IN
Chainlink: Staking
0 ETH0.0017690913.31354512
Migrate215113952024-12-29 23:24:4748 days ago1735514687IN
Chainlink: Staking
0 ETH0.000444083.34171167
Migrate214955172024-12-27 18:13:5950 days ago1735323239IN
Chainlink: Staking
0 ETH0.000972978.40372283
Migrate214899072024-12-26 23:25:1151 days ago1735255511IN
Chainlink: Staking
0 ETH0.000564434.24773673
Migrate214695072024-12-24 2:59:4754 days ago1735009187IN
Chainlink: Staking
0 ETH0.000861237.43939191
Migrate214509582024-12-21 12:43:1156 days ago1734784991IN
Chainlink: Staking
0 ETH0.001137979.82889152
Migrate214418002024-12-20 5:59:4758 days ago1734674387IN
Chainlink: Staking
0 ETH0.0014161712.23041532
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block
From
To
186719312023-11-28 18:33:11445 days ago1701196391
Chainlink: Staking
0.00707205 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Staking

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 75000 runs

Other Settings:
default evmVersion
File 1 of 21 : Staking.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import {LinkTokenInterface} from '@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol';
import {TypeAndVersionInterface} from '@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol';
import {AggregatorV3Interface} from '@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol';
import {ConfirmedOwner} from '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol';
import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';
import {MerkleProof} from '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';
import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol';
import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
import {IStaking} from './interfaces/IStaking.sol';
import {IStakingOwner} from './interfaces/IStakingOwner.sol';
import {IMerkleAccessController} from './interfaces/IMerkleAccessController.sol';
import {IAlertsController} from './interfaces/IAlertsController.sol';
import {IMigratable} from './interfaces/IMigratable.sol';
import {StakingPoolLib} from './StakingPoolLib.sol';
import {RewardLib, SafeCast} from './RewardLib.sol';

contract Staking is
  IStaking,
  IStakingOwner,
  IMigratable,
  IMerkleAccessController,
  IAlertsController,
  ConfirmedOwner,
  TypeAndVersionInterface,
  Pausable
{
  using StakingPoolLib for StakingPoolLib.Pool;
  using RewardLib for RewardLib.Reward;
  using SafeCast for uint256;

  /// @notice This struct defines the params required by the Staking contract's
  /// constructor.
  struct PoolConstructorParams {
    /// @notice The LINK Token
    LinkTokenInterface LINKAddress;
    /// @notice The feed being monitored when raising alerts
    AggregatorV3Interface monitoredFeed;
    /// @notice The initial maximum total stake amount across all stakers
    uint256 initialMaxPoolSize;
    /// @notice The initial maximum stake amount for a single community staker
    uint256 initialMaxCommunityStakeAmount;
    /// @notice The initial maximum stake amount for a single node operator
    uint256 initialMaxOperatorStakeAmount;
    /// @notice The minimum stake amount that a community staker can stake
    uint256 minCommunityStakeAmount;
    /// @notice The minimum stake amount that an operator can stake
    uint256 minOperatorStakeAmount;
    /// @notice The number of seconds until the feed is considered stale
    /// and the priority period begins.
    uint256 priorityPeriodThreshold;
    /// @notice The number of seconds until the priority period ends
    /// and the regular period begins.
    uint256 regularPeriodThreshold;
    /// @notice The amount of LINK to reward an operator who
    /// raises an alert in the priority period.
    uint256 maxAlertingRewardAmount;
    /// @notice The minimum number of node operators required to initialize the
    /// staking pool.
    uint256 minInitialOperatorCount;
    /// @notice The minimum reward duration after pool config updates and pool
    /// reward extensions
    uint256 minRewardDuration;
    /// @notice The duration of earned rewards to slash when an alert is raised
    uint256 slashableDuration;
    /// @notice Used to calculate delegated stake amount
    /// = amount / delegation rate denominator = 100% / 100 = 1%
    uint256 delegationRateDenominator;
  }

  /// @notice The amount to divide an alerter's stake amount when
  /// calculating their reward for raising an alert.
  uint256 private constant ALERTING_REWARD_STAKED_AMOUNT_DENOMINATOR = 5;

  LinkTokenInterface private immutable i_LINK;
  StakingPoolLib.Pool private s_pool;
  RewardLib.Reward private s_reward;
  /// @notice The ETH USD feed that alerters can raise alerts for.
  AggregatorV3Interface private immutable i_monitoredFeed;
  /// @notice The proposed address stakers will migrate funds to
  address private s_proposedMigrationTarget;
  /// @notice The timestamp of when the migration target was proposed at
  uint256 private s_proposedMigrationTargetAt;
  /// @notice The address stakers can migrate their funds to
  address private s_migrationTarget;
  /// @notice The round ID of the last feed round an alert was raised
  uint256 private s_lastAlertedRoundId;
  /// @notice The merkle root of the merkle tree generated from the list
  /// of staker addresses with early acccess.
  bytes32 private s_merkleRoot;
  /// @notice The number of seconds until the feed is considered stale
  /// and the priority period begins.
  uint256 private immutable i_priorityPeriodThreshold;
  /// @notice The number of seconds until the priority period ends
  /// and the regular period begins.
  uint256 private immutable i_regularPeriodThreshold;
  /// @notice The amount of LINK to reward an operator who
  /// raises an alert in the priority period.
  uint256 private immutable i_maxAlertingRewardAmount;
  /// @notice The minimum stake amount that a node operator can stake
  uint256 private immutable i_minOperatorStakeAmount;
  /// @notice The minimum stake amount that a community staker can stake
  uint256 private immutable i_minCommunityStakeAmount;
  /// @notice The minimum number of node operators required to initialize the
  /// staking pool.
  uint256 private immutable i_minInitialOperatorCount;
  /// @notice The minimum reward duration after pool config updates and pool
  /// reward extensions
  uint256 private immutable i_minRewardDuration;
  /// @notice The duration of earned rewards to slash when an alert is raised
  uint256 private immutable i_slashableDuration;
  /// @notice Used to calculate delegated stake amount
  /// = amount / delegation rate denominator = 100% / 100 = 1%
  uint256 private immutable i_delegationRateDenominator;

  constructor(PoolConstructorParams memory params) ConfirmedOwner(msg.sender) {
    if (address(params.LINKAddress) == address(0)) revert InvalidZeroAddress();
    if (address(params.monitoredFeed) == address(0))
      revert InvalidZeroAddress();
    if (params.delegationRateDenominator == 0) revert InvalidDelegationRate();
    if (RewardLib.REWARD_PRECISION % params.delegationRateDenominator > 0)
      revert InvalidDelegationRate();
    if (params.regularPeriodThreshold <= params.priorityPeriodThreshold)
      revert InvalidRegularPeriodThreshold();
    if (params.minOperatorStakeAmount == 0)
      revert InvalidMinOperatorStakeAmount();
    if (params.minOperatorStakeAmount > params.initialMaxOperatorStakeAmount)
      revert InvalidMinOperatorStakeAmount();
    if (params.minCommunityStakeAmount > params.initialMaxCommunityStakeAmount)
      revert InvalidMinCommunityStakeAmount();
    if (params.maxAlertingRewardAmount > params.initialMaxOperatorStakeAmount)
      revert InvalidMaxAlertingRewardAmount();

    s_pool._setConfig(
      params.initialMaxPoolSize,
      params.initialMaxCommunityStakeAmount,
      params.initialMaxOperatorStakeAmount
    );
    i_LINK = params.LINKAddress;
    i_monitoredFeed = params.monitoredFeed;
    i_priorityPeriodThreshold = params.priorityPeriodThreshold;
    i_regularPeriodThreshold = params.regularPeriodThreshold;
    i_maxAlertingRewardAmount = params.maxAlertingRewardAmount;
    i_minOperatorStakeAmount = params.minOperatorStakeAmount;
    i_minCommunityStakeAmount = params.minCommunityStakeAmount;
    i_minInitialOperatorCount = params.minInitialOperatorCount;
    i_minRewardDuration = params.minRewardDuration;
    i_slashableDuration = params.slashableDuration;
    i_delegationRateDenominator = params.delegationRateDenominator;
  }

  // =======================
  // TypeAndVersionInterface
  // =======================

  /// @inheritdoc TypeAndVersionInterface
  function typeAndVersion() external pure override returns (string memory) {
    return 'Staking 0.1.0';
  }

  // =================
  // IMerkleAccessController
  // =================

  /// @inheritdoc IMerkleAccessController
  function hasAccess(address staker, bytes32[] memory proof)
    external
    view
    override
    returns (bool)
  {
    if (s_merkleRoot == bytes32(0)) return true;
    return
      MerkleProof.verify(proof, s_merkleRoot, keccak256(abi.encode(staker)));
  }

  /// @inheritdoc IMerkleAccessController
  function setMerkleRoot(bytes32 newMerkleRoot) external override onlyOwner {
    s_merkleRoot = newMerkleRoot;
    emit MerkleRootChanged(newMerkleRoot);
  }

  /// @inheritdoc IMerkleAccessController
  function getMerkleRoot() external view override returns (bytes32) {
    return s_merkleRoot;
  }

  // =============
  // IStakingOwner
  // =============

  /// @inheritdoc IStakingOwner
  function setPoolConfig(
    uint256 maxPoolSize,
    uint256 maxCommunityStakeAmount,
    uint256 maxOperatorStakeAmount
  ) external override(IStakingOwner) onlyOwner whenActive {
    s_pool._setConfig(
      maxPoolSize,
      maxCommunityStakeAmount,
      maxOperatorStakeAmount
    );

    s_reward._updateDuration(
      maxPoolSize,
      s_pool._getTotalStakedAmount(),
      uint256(s_reward.base.rate),
      i_minRewardDuration,
      getAvailableReward(),
      getTotalDelegatedAmount()
    );
  }

  /// @inheritdoc IStakingOwner
  function setFeedOperators(address[] calldata operators)
    external
    override(IStakingOwner)
    onlyOwner
  {
    s_pool._setFeedOperators(operators);
  }

  /// @inheritdoc IStakingOwner
  function start(uint256 amount, uint256 initialRewardRate)
    external
    override(IStakingOwner)
    onlyOwner
  {
    if (s_merkleRoot == bytes32(0)) revert MerkleRootNotSet();

    s_pool._open(i_minInitialOperatorCount);

    // We need to transfer LINK balance before we initialize the reward to
    // calculate the new reward expiry timestamp.
    i_LINK.transferFrom(msg.sender, address(this), amount);

    s_reward._initialize(
      uint256(s_pool.limits.maxPoolSize),
      initialRewardRate,
      i_minRewardDuration,
      getAvailableReward()
    );
  }

  /// @inheritdoc IStakingOwner
  function conclude() external override(IStakingOwner) onlyOwner whenActive {
    s_reward._release(
      s_pool._getTotalStakedAmount(),
      getTotalDelegatedAmount()
    );

    s_pool._close();
  }

  /// @inheritdoc IStakingOwner
  function addReward(uint256 amount)
    external
    override(IStakingOwner)
    onlyOwner
    whenActive
  {
    // We need to transfer LINK balance before we recalculate the reward expiry
    // timestamp so the new amount is accounted for.
    i_LINK.transferFrom(msg.sender, address(this), amount);

    s_reward._updateDuration(
      uint256(s_pool.limits.maxPoolSize),
      s_pool._getTotalStakedAmount(),
      uint256(s_reward.base.rate),
      i_minRewardDuration,
      getAvailableReward(),
      getTotalDelegatedAmount()
    );

    emit RewardLib.RewardAdded(amount);
  }

  /// @inheritdoc IStakingOwner
  function withdrawUnusedReward()
    external
    override(IStakingOwner)
    onlyOwner
    whenInactive
  {
    uint256 unusedRewards = getAvailableReward() -
      uint256(s_reward.reserved.base) -
      uint256(s_reward.reserved.delegated);
    emit RewardLib.RewardWithdrawn(unusedRewards);

    // msg.sender is the owner address as only the owner can call this function
    i_LINK.transfer(msg.sender, unusedRewards);
  }

  /// @dev Required conditions for adding operators:
  /// - Operators can only be added to the pool if they have no prior stake.
  /// - Operators can only be readded to the pool if they have no removed
  /// stake.
  /// - Operators cannot be added to the pool after staking ends (either through
  /// conclusion or through reward expiry).
  /// @inheritdoc IStakingOwner
  function addOperators(address[] calldata operators)
    external
    override(IStakingOwner)
    onlyOwner
  {
    // If reward was initialized (meaning the pool was active) but the pool is
    // no longer active we want to prevent adding new operators.
    if (s_reward.startTimestamp > 0 && !isActive())
      revert StakingPoolLib.InvalidPoolStatus(false, true);

    s_pool._addOperators(operators);
  }

  /// @inheritdoc IStakingOwner
  function removeOperators(address[] calldata operators)
    external
    override(IStakingOwner)
    onlyOwner
    whenActive
  {
    // Accumulate delegation rewards before removing operators as this affects
    // rewards that are distributed to remaining operators.
    s_reward._accumulateDelegationRewards(getTotalDelegatedAmount());

    for (uint256 i; i < operators.length; i++) {
      address operator = operators[i];
      StakingPoolLib.Staker memory staker = s_pool.stakers[operator];

      if (!staker.isOperator)
        revert StakingPoolLib.OperatorDoesNotExist(operator);

      // Operator must not be on the feed
      if (staker.isFeedOperator)
        revert StakingPoolLib.OperatorIsAssignedToFeed(operator);

      uint256 principal = staker.stakedAmount;
      // An operator with stake is a delegate
      if (principal > 0) {
        // The operator's rewards are forfeited when they are removed
        // Unreserve operator's earned base reward
        s_reward.reserved.base -= getBaseReward(operator)._toUint96();
        // Unreserve operator's future base reward
        s_reward.reserved.base -= s_reward
          ._calculateReward(principal, s_reward._getRemainingDuration())
          ._toUint96();

        // Unreserve operator's earned delegation reward. We don't need to
        // unreserve future delegation rewards because they will be split by
        // other operators.
        s_reward.reserved.delegated -= getDelegationReward(operator)
          ._toUint96();

        s_reward.delegated.delegatesCount -= 1;
        delete s_pool.stakers[operator].stakedAmount;
        uint96 castPrincipal = principal._toUint96();
        s_pool.state.totalOperatorStakedAmount -= castPrincipal;
        // Only the operator's principal is withdrawable after they are removed
        s_pool.stakers[operator].removedStakeAmount = castPrincipal;
        s_pool.totalOperatorRemovedAmount += castPrincipal;

        // We need to reset operator's missed base rewards in case they decide
        // to stake as a community staker using the same address. It's fine to
        // not reset missed delegated rewards, because a removed operator
        // cannot be re-added as operator again.
        delete s_reward.missed[operator].base;
      }

      s_pool.stakers[operator].isOperator = false;
      emit StakingPoolLib.OperatorRemoved(operator, principal);
    }

    s_pool.state.operatorsCount -= operators.length._toUint8();
  }

  /// @inheritdoc IStakingOwner
  function changeRewardRate(uint256 newRate)
    external
    override
    onlyOwner
    whenActive
  {
    if (newRate == 0) revert();

    uint256 totalDelegatedAmount = getTotalDelegatedAmount();

    s_reward._accumulateDelegationRewards(totalDelegatedAmount);
    s_reward._accumulateBaseRewards();
    s_reward._updateDuration(
      uint256(s_pool.limits.maxPoolSize),
      s_pool._getTotalStakedAmount(),
      newRate,
      i_minRewardDuration,
      getAvailableReward(),
      totalDelegatedAmount
    );

    emit RewardLib.RewardRateChanged(newRate);
  }

  /// @inheritdoc IStakingOwner
  function emergencyPause() external override(IStakingOwner) onlyOwner {
    _pause();
  }

  /// @inheritdoc IStakingOwner
  function emergencyUnpause() external override(IStakingOwner) onlyOwner {
    _unpause();
  }

  /// @inheritdoc IStakingOwner
  function getFeedOperators()
    external
    view
    override(IStakingOwner)
    returns (address[] memory)
  {
    return s_pool.feedOperators;
  }

  // ===========
  // IMigratable
  // ===========

  /// @inheritdoc IMigratable
  function getMigrationTarget()
    external
    view
    override(IMigratable)
    returns (address)
  {
    return s_migrationTarget;
  }

  /// @inheritdoc IMigratable
  function proposeMigrationTarget(address migrationTarget)
    external
    override(IMigratable)
    onlyOwner
  {
    if (
      migrationTarget.code.length == 0 ||
      migrationTarget == address(this) ||
      s_proposedMigrationTarget == migrationTarget ||
      s_migrationTarget == migrationTarget ||
      !IERC165(migrationTarget).supportsInterface(this.onTokenTransfer.selector)
    ) revert InvalidMigrationTarget();

    s_migrationTarget = address(0);
    s_proposedMigrationTarget = migrationTarget;
    s_proposedMigrationTargetAt = block.timestamp;
    emit MigrationTargetProposed(migrationTarget);
  }

  /// @inheritdoc IMigratable
  function acceptMigrationTarget() external override(IMigratable) onlyOwner {
    if (s_proposedMigrationTarget == address(0))
      revert InvalidMigrationTarget();

    if (block.timestamp < (uint256(s_proposedMigrationTargetAt) + 7 days))
      revert AccessForbidden();

    s_migrationTarget = s_proposedMigrationTarget;
    s_proposedMigrationTarget = address(0);
    emit MigrationTargetAccepted(s_migrationTarget);
  }

  /// @inheritdoc IMigratable
  function migrate(bytes calldata data)
    external
    override(IMigratable)
    whenInactive
  {
    if (s_migrationTarget == address(0)) revert InvalidMigrationTarget();

    (uint256 amount, uint256 baseReward, uint256 delegationReward) = _exit(
      msg.sender
    );

    emit Migrated(msg.sender, amount, baseReward, delegationReward, data);

    i_LINK.transferAndCall(
      s_migrationTarget,
      uint256(amount + baseReward + delegationReward),
      abi.encode(msg.sender, data)
    );
  }

  // =================
  // IAlertsController
  // =================

  /// @inheritdoc IAlertsController
  function raiseAlert() external override(IAlertsController) whenActive {
    uint256 stakedAmount = getStake(msg.sender);
    if (stakedAmount == 0) revert AccessForbidden();

    (uint256 roundId, , , uint256 lastFeedUpdatedAt, ) = i_monitoredFeed
      .latestRoundData();

    if (roundId == s_lastAlertedRoundId) revert AlertAlreadyExists(roundId);

    if (block.timestamp < lastFeedUpdatedAt + i_priorityPeriodThreshold)
      revert AlertInvalid();

    bool isInPriorityPeriod = block.timestamp <
      lastFeedUpdatedAt + i_regularPeriodThreshold;

    if (isInPriorityPeriod && !s_pool._isOperator(msg.sender))
      revert AlertInvalid();

    s_lastAlertedRoundId = roundId;

    // There is a risk that this might get us below the total amount of
    // reserved if the reward amount slashed is greater than LINK
    // balance in the pool.  This is an extreme edge case that will only occur
    /// if an alert is raised many times such that it completely depletes the
    // available rewards in the pool.  As this is an unlikely scenario, the
    // contract avoids adding an extra check to minimize gas costs.
    // There is a similar edge case when the total slashed amount is less than
    // the alerting reward. This can happen because slashed amounts are capped to
    // earned rewards so far. The result is a net outflow of rewards from the
    // staking pool up to the max alerting reward amount in the worst case.
    // This is acceptable and in practice has little to no impact to staking.
    uint256 rewardAmount = _calculateAlertingRewardAmount(
      stakedAmount,
      isInPriorityPeriod
    );

    emit AlertRaised(msg.sender, roundId, rewardAmount);

    // We need to transfer the rewards out before recalculating the new reward
    // expiry timestamp
    i_LINK.transfer(msg.sender, rewardAmount);

    s_reward._slashOnFeedOperators(
      i_minOperatorStakeAmount,
      i_slashableDuration,
      s_pool.feedOperators,
      s_pool.stakers,
      getTotalDelegatedAmount()
    );

    s_reward._updateDuration(
      uint256(s_pool.limits.maxPoolSize),
      s_pool._getTotalStakedAmount(),
      uint256(s_reward.base.rate),
      0,
      getAvailableReward(),
      getTotalDelegatedAmount()
    );
  }

  /// @inheritdoc IAlertsController
  function canAlert(address alerter)
    external
    view
    override(IAlertsController)
    returns (bool)
  {
    if (getStake(alerter) == 0) return false;
    if (!isActive()) return false;
    (uint256 roundId, , , uint256 updatedAt, ) = i_monitoredFeed
      .latestRoundData();
    if (roundId == s_lastAlertedRoundId) return false;

    // nobody can (feed is not stale)
    if (block.timestamp < updatedAt + i_priorityPeriodThreshold) return false;

    // all stakers can (regular alerters)
    if (block.timestamp >= updatedAt + i_regularPeriodThreshold) return true;
    return s_pool._isOperator(alerter); // only operators can (priority alerters)
  }

  // ========
  // IStaking
  // ========

  /// @inheritdoc IStaking
  function unstake() external override(IStaking) whenInactive {
    (uint256 amount, uint256 baseReward, uint256 delegationReward) = _exit(
      msg.sender
    );

    emit Unstaked(msg.sender, amount, baseReward, delegationReward);
    i_LINK.transfer(msg.sender, amount + baseReward + delegationReward);
  }

  /// @inheritdoc IStaking
  function withdrawRemovedStake() external override(IStaking) whenInactive {
    uint256 amount = s_pool.stakers[msg.sender].removedStakeAmount;
    if (amount == 0) revert StakingPoolLib.StakeNotFound(msg.sender);

    s_pool.totalOperatorRemovedAmount -= amount;
    delete s_pool.stakers[msg.sender].removedStakeAmount;
    emit Unstaked(msg.sender, amount, 0, 0);
    i_LINK.transfer(msg.sender, amount);
  }

  /// @inheritdoc IStaking
  function getStake(address staker)
    public
    view
    override(IStaking)
    returns (uint256)
  {
    return s_pool.stakers[staker].stakedAmount;
  }

  /// @inheritdoc IStaking
  function isOperator(address staker)
    external
    view
    override(IStaking)
    returns (bool)
  {
    return s_pool._isOperator(staker);
  }

  /// @inheritdoc IStaking
  function isActive() public view override(IStaking) returns (bool) {
    return s_pool.state.isOpen && !s_reward._isDepleted();
  }

  /// @inheritdoc IStaking
  function getMaxPoolSize() external view override(IStaking) returns (uint256) {
    return uint256(s_pool.limits.maxPoolSize);
  }

  /// @inheritdoc IStaking
  function getCommunityStakerLimits()
    external
    view
    override(IStaking)
    returns (uint256, uint256)
  {
    return (
      i_minCommunityStakeAmount,
      uint256(s_pool.limits.maxCommunityStakeAmount)
    );
  }

  /// @inheritdoc IStaking
  function getOperatorLimits()
    external
    view
    override(IStaking)
    returns (uint256, uint256)
  {
    return (
      i_minOperatorStakeAmount,
      uint256(s_pool.limits.maxOperatorStakeAmount)
    );
  }

  /// @inheritdoc IStaking
  function getRewardTimestamps()
    external
    view
    override(IStaking)
    returns (uint256, uint256)
  {
    return (uint256(s_reward.startTimestamp), uint256(s_reward.endTimestamp));
  }

  /// @inheritdoc IStaking
  function getRewardRate() external view override(IStaking) returns (uint256) {
    return uint256(s_reward.base.rate);
  }

  /// @inheritdoc IStaking
  function getDelegationRateDenominator()
    external
    view
    override(IStaking)
    returns (uint256)
  {
    return i_delegationRateDenominator;
  }

  /// @inheritdoc IStaking
  function getAvailableReward()
    public
    view
    override(IStaking)
    returns (uint256)
  {
    return
      i_LINK.balanceOf(address(this)) -
      s_pool._getTotalStakedAmount() -
      s_pool.totalOperatorRemovedAmount;
  }

  /// @inheritdoc IStaking
  function getBaseReward(address staker)
    public
    view
    override(IStaking)
    returns (uint256)
  {
    uint256 stake = s_pool.stakers[staker].stakedAmount;
    if (stake == 0) return 0;

    if (s_pool._isOperator(staker)) {
      return s_reward._getOperatorEarnedBaseRewards(staker, stake);
    }

    return
      s_reward._calculateAccruedBaseRewards(
        RewardLib._getNonDelegatedAmount(stake, i_delegationRateDenominator)
      ) - uint256(s_reward.missed[staker].base);
  }

  /// @inheritdoc IStaking
  function getDelegationReward(address staker)
    public
    view
    override(IStaking)
    returns (uint256)
  {
    StakingPoolLib.Staker memory stakerAccount = s_pool.stakers[staker];
    if (!stakerAccount.isOperator) return 0;
    if (stakerAccount.stakedAmount == 0) return 0;
    return
      s_reward._getOperatorEarnedDelegatedRewards(
        staker,
        getTotalDelegatedAmount()
      );
  }

  /// @inheritdoc IStaking
  function getTotalDelegatedAmount()
    public
    view
    override(IStaking)
    returns (uint256)
  {
    return
      RewardLib._getDelegatedAmount(
        s_pool.state.totalCommunityStakedAmount,
        i_delegationRateDenominator
      );
  }

  /// @inheritdoc IStaking
  function getDelegatesCount()
    external
    view
    override(IStaking)
    returns (uint256)
  {
    return uint256(s_reward.delegated.delegatesCount);
  }

  /// @inheritdoc IStaking
  function getTotalStakedAmount()
    external
    view
    override(IStaking)
    returns (uint256)
  {
    return s_pool._getTotalStakedAmount();
  }

  /// @inheritdoc IStaking
  function getTotalCommunityStakedAmount()
    external
    view
    override(IStaking)
    returns (uint256)
  {
    return s_pool.state.totalCommunityStakedAmount;
  }

  /// @inheritdoc IStaking
  function getTotalRemovedAmount()
    external
    view
    override(IStaking)
    returns (uint256)
  {
    return s_pool.totalOperatorRemovedAmount;
  }

  /// @inheritdoc IStaking
  function getEarnedBaseRewards()
    external
    view
    override(IStaking)
    returns (uint256)
  {
    return
      s_reward._getEarnedBaseRewards(
        s_pool._getTotalStakedAmount(),
        getTotalDelegatedAmount()
      );
  }

  /// @inheritdoc IStaking
  function getEarnedDelegationRewards()
    external
    view
    override(IStaking)
    returns (uint256)
  {
    return s_reward._getEarnedDelegationRewards(getTotalDelegatedAmount());
  }

  /// @inheritdoc IStaking
  function isPaused() external view override(IStaking) returns (bool) {
    return paused();
  }

  /// @inheritdoc IStaking
  function getChainlinkToken()
    public
    view
    override(IStaking)
    returns (address)
  {
    return address(i_LINK);
  }

  /// @inheritdoc IStaking
  function getMonitoredFeed() external view override returns (address) {
    return address(i_monitoredFeed);
  }

  /**
   * @notice Called when LINK is sent to the contract via `transferAndCall`
   * @param sender Address of the sender
   * @param amount Amount of LINK sent (specified in wei)
   * @param data Optional payload containing a Staking Allowlist Merkle proof
   */
  function onTokenTransfer(
    address sender,
    uint256 amount,
    bytes memory data
  ) external validateFromLINK whenNotPaused whenActive {
    if (amount < RewardLib.REWARD_PRECISION)
      revert StakingPoolLib.InsufficientStakeAmount(RewardLib.REWARD_PRECISION);

    // TL;DR: Reward calculation and delegation logic requires precise numbers
    // to avoid cumulative rounding errors.
    // Long explanation:
    // When users stake amounts that are rounded down to 0 after dividing
    // by the delegation rate denominator, not enough rewards are reserved for
    // the user. When the user then stakes enough times, small rounding errors
    // accumulate. This causes an integer underflow when unreserving rewards because
    // the total delegated amount returns a larger number than what individual
    // reserved amounts sum up to.
    uint256 remainder = amount % RewardLib.REWARD_PRECISION;
    if (remainder > 0) {
      amount -= remainder;
      i_LINK.transfer(sender, remainder);
    }

    if (s_pool._isOperator(sender)) {
      _stakeAsOperator(sender, amount);
    } else {
      // If a Merkle root is set, the sender should
      // prove that they are part of the merkle tree
      if (s_merkleRoot != bytes32(0)) {
        if (data.length == 0) revert AccessForbidden();
        if (
          !MerkleProof.verify(
            abi.decode(data, (bytes32[])),
            s_merkleRoot,
            keccak256(abi.encode(sender))
          )
        ) revert AccessForbidden();
      }
      _stakeAsCommunityStaker(sender, amount);
    }
  }

  // =======
  // Private
  // =======

  /// @notice Helper function for when a community staker enters the pool
  /// @param staker The staker address
  /// @param amount The amount of principal staked
  /// @dev When an operator is removed they can stake as a community staker.
  /// We allow that because the alternative (checking for removed stake before
  /// staking) is going to unnecessarily increase gas costs in 99.99% of the
  /// cases.
  function _stakeAsCommunityStaker(address staker, uint256 amount) private {
    uint256 currentStakedAmount = s_pool.stakers[staker].stakedAmount;
    uint256 newStakedAmount = currentStakedAmount + amount;
    // Check that the amount is greater than or equal to the minimum required
    if (newStakedAmount < i_minCommunityStakeAmount)
      revert StakingPoolLib.InsufficientStakeAmount(i_minCommunityStakeAmount);

    // Check that the amount is less than or equal to the maximum allowed
    uint256 maxCommunityStakeAmount = uint256(
      s_pool.limits.maxCommunityStakeAmount
    );
    if (newStakedAmount > maxCommunityStakeAmount)
      revert StakingPoolLib.ExcessiveStakeAmount(
        maxCommunityStakeAmount - currentStakedAmount
      );

    // Check if the amount supplied increases the total staked amount above
    // the maximum pool size
    uint256 remainingPoolSpace = s_pool._getRemainingPoolSpace();
    if (amount > remainingPoolSpace)
      revert StakingPoolLib.ExcessiveStakeAmount(remainingPoolSpace);

    s_reward._accumulateDelegationRewards(getTotalDelegatedAmount());
    uint256 extraNonDelegatedAmount = RewardLib._getNonDelegatedAmount(
      amount,
      i_delegationRateDenominator
    );
    s_reward.missed[staker].base += s_reward
      ._calculateAccruedBaseRewards(extraNonDelegatedAmount)
      ._toUint96();
    s_reward._reserve(
      extraNonDelegatedAmount,
      RewardLib._getDelegatedAmount(amount, i_delegationRateDenominator)
    );
    s_pool.state.totalCommunityStakedAmount += amount._toUint96();
    s_pool.stakers[staker].stakedAmount = newStakedAmount._toUint96();
    emit Staked(staker, amount, newStakedAmount);
  }

  /// @notice Helper function for when an operator enters the pool
  /// @dev Function skips validating whether or not the operator stake
  /// amount will cause the total stake amount to exceed the maximum pool size.
  /// This is because the pool already reserves a fixed amount of space
  /// for each operator meaning that an operator staking cannot cause the
  /// total stake amount to exceed the maximum pool size.  Each operator
  /// receives a reserved stake amount equal to the maxOperatorStakeAmount.
  /// This is done by deducting operatorCount * maxOperatorStakeAmount from the
  /// remaining pool space available for staking.
  /// @param staker The staker address
  /// @param amount The amount of principal staked
  function _stakeAsOperator(address staker, uint256 amount) private {
    StakingPoolLib.Staker storage operator = s_pool.stakers[staker];
    uint256 currentStakedAmount = operator.stakedAmount;
    uint256 newStakedAmount = currentStakedAmount + amount;

    // Check that the amount is greater than or equal to the minimum required
    if (newStakedAmount < i_minOperatorStakeAmount)
      revert StakingPoolLib.InsufficientStakeAmount(i_minOperatorStakeAmount);

    // Check that the amount is less than or equal to the maximum allowed
    uint256 maxOperatorStakeAmount = uint256(
      s_pool.limits.maxOperatorStakeAmount
    );
    if (newStakedAmount > maxOperatorStakeAmount)
      revert StakingPoolLib.ExcessiveStakeAmount(
        maxOperatorStakeAmount - currentStakedAmount
      );

    // On first stake
    if (currentStakedAmount == 0) {
      s_reward._accumulateDelegationRewards(getTotalDelegatedAmount());
      uint8 delegatesCount = s_reward.delegated.delegatesCount;

      // We are doing this check to unreserve any unused delegated rewards
      // prior to the first operator staking. After the rewards are unreserved
      // we reset the accumulated value so it doesn't count towards missed
      // rewards.
      // There is a known edge-case where, if no operator stakes throughout the
      // duration of the pool, we wouldn't unreserve unused delegation rewards.
      // In practice this shouldn't happen and, if it does, there are
      // operational workarounds to unreserve those rewards.
      if (delegatesCount == 0) {
        s_reward.reserved.delegated -= s_reward.delegated.cumulativePerDelegate;
        delete s_reward.delegated.cumulativePerDelegate;
      }

      s_reward.delegated.delegatesCount = delegatesCount + 1;

      s_reward.missed[staker].delegated = s_reward
        .delegated
        .cumulativePerDelegate;
    }

    s_reward.missed[staker].base += s_reward
      ._calculateAccruedBaseRewards(amount)
      ._toUint96();
    s_pool.state.totalOperatorStakedAmount += amount._toUint96();
    s_reward._reserve(amount, 0);
    s_pool.stakers[staker].stakedAmount = newStakedAmount._toUint96();
    emit Staked(staker, amount, newStakedAmount);
  }

  /// @notice Helper function when staker exits the pool
  /// @param staker The staker address
  function _exit(address staker)
    private
    returns (
      uint256,
      uint256,
      uint256
    )
  {
    StakingPoolLib.Staker memory stakerAccount = s_pool.stakers[staker];
    if (stakerAccount.stakedAmount == 0)
      revert StakingPoolLib.StakeNotFound(staker);

    // If the pool isOpen that means that we haven't concluded it and stakers
    // got here because the reward depleted. In that case, the first user to
    // unstake will accumulate delegation and base rewards to save on cost for
    // others.
    if (s_pool.state.isOpen) {
      // Accumulate base and delegation rewards before unreserving rewards to
      // save gas costs. We can use the accumulated reward per micro LINK and
      // accumulated delegation reward to simplify reward calculations.
      s_reward._accumulateDelegationRewards(getTotalDelegatedAmount());
      s_reward._accumulateBaseRewards();
      delete s_pool.state.isOpen;
    }

    if (stakerAccount.isOperator) {
      s_pool.state.totalOperatorStakedAmount -= stakerAccount.stakedAmount;

      uint256 baseReward = s_reward._calculateConcludedBaseRewards(
        stakerAccount.stakedAmount,
        staker
      );
      uint256 delegationReward = uint256(
        s_reward.delegated.cumulativePerDelegate
      ) - uint256(s_reward.missed[staker].delegated);

      delete s_pool.stakers[staker].stakedAmount;
      s_reward.reserved.base -= baseReward._toUint96();
      s_reward.reserved.delegated -= delegationReward._toUint96();
      return (stakerAccount.stakedAmount, baseReward, delegationReward);
    } else {
      s_pool.state.totalCommunityStakedAmount -= stakerAccount.stakedAmount;

      uint256 baseReward = s_reward._calculateConcludedBaseRewards(
        RewardLib._getNonDelegatedAmount(
          stakerAccount.stakedAmount,
          i_delegationRateDenominator
        ),
        staker
      );
      delete s_pool.stakers[staker].stakedAmount;
      s_reward.reserved.base -= baseReward._toUint96();
      return (stakerAccount.stakedAmount, baseReward, 0);
    }
  }

  /// @notice Calculates the reward amount an alerter will receive for raising
  /// a successful alert in the current alerting period.
  /// @param stakedAmount Amount of LINK staked by the alerter
  /// @param isInPriorityPeriod True if it is currently in the priority period
  /// @return rewardAmount Amount of LINK rewards to be paid to the alerter
  function _calculateAlertingRewardAmount(
    uint256 stakedAmount,
    bool isInPriorityPeriod
  ) private view returns (uint256) {
    if (isInPriorityPeriod) return i_maxAlertingRewardAmount;
    return
      Math.min(
        stakedAmount / ALERTING_REWARD_STAKED_AMOUNT_DENOMINATOR,
        i_maxAlertingRewardAmount
      );
  }

  // =========
  // Modifiers
  // =========

  /// @dev Having a private function for the modifer saves on the contract size
  function _isActive() private view {
    if (!isActive()) revert StakingPoolLib.InvalidPoolStatus(false, true);
  }

  /// @dev Reverts if the staking pool is inactive (not open for staking or
  /// expired)
  modifier whenActive() {
    _isActive();

    _;
  }

  /// @dev Reverts if the staking pool is active (open for staking)
  modifier whenInactive() {
    if (isActive()) revert StakingPoolLib.InvalidPoolStatus(true, false);

    _;
  }

  /// @dev Reverts if not sent from the LINK token
  modifier validateFromLINK() {
    if (msg.sender != getChainlinkToken()) revert SenderNotLinkToken();

    _;
  }
}

File 2 of 21 : RewardLib.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import {SafeCast} from './SafeCast.sol';
import {StakingPoolLib} from './StakingPoolLib.sol';
import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

library RewardLib {
  using SafeCast for uint256;

  /// @notice emitted when the reward is initialized for the first time
  /// @param rate the reward rate
  /// @param available the amount of rewards available for distribution in the
  /// staking pool
  /// @param startTimestamp the start timestamp when rewards are started
  /// @param endTimestamp the timestamp when the reward will run out
  event RewardInitialized(
    uint256 rate,
    uint256 available,
    uint256 startTimestamp,
    uint256 endTimestamp
  );
  /// @notice emitted when owner changes the reward rate
  /// @param rate the new reward rate
  event RewardRateChanged(uint256 rate);
  /// @notice emitted when owner adds more rewards to the pool
  /// @param amountAdded the amount of LINK rewards added to the pool
  event RewardAdded(uint256 amountAdded);
  /// @notice emitted when owner withdraws unreserved rewards
  /// @param amount the amount of rewards withdrawn
  event RewardWithdrawn(uint256 amount);
  /// @notice emitted when an on feed operator gets slashed.
  /// Node operators are not slashed more than the amount of rewards they
  /// have earned.  This means that a node operator that has not
  /// accumulated at least two weeks of rewards will be slashed
  /// less than an operator that has accumulated at least
  /// two weeks of rewards.
  event RewardSlashed(
    address[] operator,
    uint256[] slashedBaseRewards,
    uint256[] slashedDelegatedRewards
  );

  /// @notice This error is thrown when the updated reward duration is less than a month
  error RewardDurationTooShort();

  /// @notice This is the reward calculation precision variable. LINK token has the
  /// 1e18 multiplier which means that rewards are floored after 6 decimals
  /// points. Micro LINK is the smallest unit that is eligible for rewards.
  uint256 internal constant REWARD_PRECISION = 1e12;

  struct DelegatedRewards {
    // Count of delegates who are eligible for a share of a reward
    // This is always going to be less or equal to operatorsCount
    uint8 delegatesCount;
    // Tracks base reward amounts that goes to an operator as delegation rewards.
    // Used to correctly account for any changes in operator count, delegated amount, or reward rate.
    // Formula: duration * rate * amount
    uint96 cumulativePerDelegate;
    // Timestamp of the last time accumulate was called
    // `startTimestamp` <= `delegated.lastAccumulateTimestamp`
    uint32 lastAccumulateTimestamp;
  }

  struct BaseRewards {
    // Reward rate expressed in juels per second per micro LINK
    uint80 rate;
    // The cumulative LINK accrued per stake from past reward rates
    // expressed in juels per micro LINK
    // Formula: sum of (previousRate * elapsedDurationSinceLastAccumulate)
    uint96 cumulativePerMicroLINK;
    // Timestamp of the last time the base reward rate was accumulated
    uint32 lastAccumulateTimestamp;
  }

  struct MissedRewards {
    // Tracks missed base rewards that are deducted from late stakers
    uint96 base;
    // Tracks missed delegation rewards that are deducted from late delegates
    uint96 delegated;
  }

  struct ReservedRewards {
    // Tracks base reward amount reserved for stakers. This can be used after
    // `endTimestamp` to calculate unused amount.
    // This amount accumulates as the reward is utilized.
    // Formula: duration * rate * amount
    uint96 base;
    // Tracks delegated reward amount reserved for node operators. This can
    // be used after `endTimestamp` to calculate unused amount.
    // This amount accumulates as the reward is utilized.
    // Formula: duration * rate * amount
    uint96 delegated;
  }

  struct Reward {
    mapping(address => MissedRewards) missed;
    DelegatedRewards delegated;
    BaseRewards base;
    ReservedRewards reserved;
    // Timestamp when the reward stops accumulating. Has to support a very long
    // duration for scenarios with low reward rate.
    // `endTimestamp` >= `startTimestamp`
    uint256 endTimestamp;
    // Timestamp when the reward comes into effect
    // `startTimestamp` <= `endTimestamp`
    uint32 startTimestamp;
  }

  /// @notice initializes the reward with the defined parameters
  /// @param maxPoolSize maximum pool size that the reward is initialized with
  /// @param rate reward rate
  /// @param minRewardDuration the minimum duration rewards need to last for
  /// @param availableReward available reward amount
  /// @dev can only be called once. Any future reward changes have to be done
  /// using specific functions.
  function _initialize(
    Reward storage reward,
    uint256 maxPoolSize,
    uint256 rate,
    uint256 minRewardDuration,
    uint256 availableReward
  ) internal {
    if (reward.startTimestamp != 0) revert();

    reward.base.rate = rate._toUint80();

    uint32 blockTimestamp = block.timestamp._toUint32();
    reward.startTimestamp = blockTimestamp;
    reward.delegated.lastAccumulateTimestamp = blockTimestamp;
    reward.base.lastAccumulateTimestamp = blockTimestamp;

    _updateDuration(
      reward,
      maxPoolSize,
      0,
      rate,
      minRewardDuration,
      availableReward,
      0
    );

    emit RewardInitialized(
      rate,
      availableReward,
      reward.startTimestamp,
      reward.endTimestamp
    );
  }

  /// @return bool true if the reward is expired (end <= now)
  function _isDepleted(Reward storage reward) internal view returns (bool) {
    return reward.endTimestamp <= block.timestamp;
  }

  /// @notice Helper function to accumulate base rewards
  /// Accumulate reward per micro LINK before changing reward rate.
  /// This keeps rewards prior to rate change unaffected.
  function _accumulateBaseRewards(Reward storage reward) internal {
    uint256 cappedTimestamp = _getCappedTimestamp(reward);

    reward.base.cumulativePerMicroLINK += (uint256(reward.base.rate) *
      (cappedTimestamp - uint256(reward.base.lastAccumulateTimestamp)))
      ._toUint96();
    reward.base.lastAccumulateTimestamp = cappedTimestamp._toUint32();
  }

  /// @notice Helper function to accumulate delegation rewards
  /// @dev This function is necessary to correctly account for any changes in
  /// eligible operators, delegated amount or reward rate.
  function _accumulateDelegationRewards(
    Reward storage reward,
    uint256 delegatedAmount
  ) internal {
    reward.delegated.cumulativePerDelegate = _calculateAccruedDelegatedRewards(
      reward,
      delegatedAmount
    )._toUint96();

    reward.delegated.lastAccumulateTimestamp = _getCappedTimestamp(reward)
      ._toUint32();
  }

  /// @notice Helper function to calculate rewards
  /// @param amount a staked amount to calculate rewards for
  /// @param duration a duration that the specified amount receives rewards for
  /// @return rewardsAmount
  function _calculateReward(
    Reward storage reward,
    uint256 amount,
    uint256 duration
  ) internal view returns (uint256) {
    return (amount * uint256(reward.base.rate) * duration) / REWARD_PRECISION;
  }

  /// @notice Calculates the amount of delegated rewards accumulated so far.
  /// @dev This function takes into account the amount of delegated
  /// rewards accumulated from previous delegate counts and amounts and
  /// the latest additional value.
  function _calculateAccruedDelegatedRewards(
    Reward storage reward,
    uint256 totalDelegatedAmount
  ) internal view returns (uint256) {
    uint256 elapsedDurationSinceLastAccumulate = _isDepleted(reward)
      ? uint256(reward.endTimestamp) -
        uint256(reward.delegated.lastAccumulateTimestamp)
      : block.timestamp - uint256(reward.delegated.lastAccumulateTimestamp);

    return
      uint256(reward.delegated.cumulativePerDelegate) +
      _calculateReward(
        reward,
        totalDelegatedAmount,
        elapsedDurationSinceLastAccumulate
      ) /
      // We are doing this to keep track of delegated rewards prior to the
      // first operator staking.
      Math.max(uint256(reward.delegated.delegatesCount), 1);
  }

  /// @notice Calculates the amount of rewards accrued so far.
  /// @dev This function takes into account the amount of
  /// rewards accumulated from previous rates in addition to
  /// the rewards that will be accumulated based off the current rate
  /// over a given duration.
  function _calculateAccruedBaseRewards(Reward storage reward, uint256 amount)
    internal
    view
    returns (uint256)
  {
    uint256 elapsedDurationSinceLastAccumulate = _isDepleted(reward)
      ? (uint256(reward.endTimestamp) -
        uint256(reward.base.lastAccumulateTimestamp))
      : block.timestamp - uint256(reward.base.lastAccumulateTimestamp);

    return
      (amount *
        (uint256(reward.base.cumulativePerMicroLINK) +
          uint256(reward.base.rate) *
          elapsedDurationSinceLastAccumulate)) / REWARD_PRECISION;
  }

  /// @notice We use a simplified reward calculation formula because we know that
  /// the reward is expired. We accumulate reward per micro LINK
  /// before concluding the pool so we can avoid reading additional storage
  /// variables.
  function _calculateConcludedBaseRewards(
    Reward storage reward,
    uint256 amount,
    address staker
  ) internal view returns (uint256) {
    return
      (amount * uint256(reward.base.cumulativePerMicroLINK)) /
      REWARD_PRECISION -
      uint256(reward.missed[staker].base);
  }

  /// @notice Reserves staker rewards. This is necessary to make sure that
  /// there are always enough available LINK tokens for all stakers until the
  /// reward end timestamp. The amount is calculated for the remaining reward
  /// duration using the current reward rate.
  /// @param baseRewardAmount The amount of base rewards to reserve
  /// or unreserve for
  /// @param delegatedRewardAmount The amount of delegated rewards to reserve
  /// or unreserve for
  /// @param isReserving true if function should reserve more rewards. false will
  /// unreserve and deduct from the reserved total
  function _updateReservedRewards(
    Reward storage reward,
    uint256 baseRewardAmount,
    uint256 delegatedRewardAmount,
    bool isReserving
  ) private {
    uint256 duration = _getRemainingDuration(reward);
    uint96 deltaBaseReward = _calculateReward(
      reward,
      baseRewardAmount,
      duration
    )._toUint96();
    uint96 deltaDelegatedReward = _calculateReward(
      reward,
      delegatedRewardAmount,
      duration
    )._toUint96();
    // add if is reserving, subtract otherwise
    if (isReserving) {
      // We round up (by adding an extra juels) if the amount includes an
      // increment below REWARD_PRECISION. We always need to reserve more than
      // the user will earn. The consequence of this is that we’ll have dust
      // LINK amounts left over in the contract after stakers exit. The amount
      // will be approximately 1 juels for every call to reserve function,
      // which translates to <1 LINK for the duration of staking v0.1 contract.
      if (baseRewardAmount % REWARD_PRECISION > 0) deltaBaseReward++;
      if (delegatedRewardAmount % REWARD_PRECISION > 0) deltaDelegatedReward++;

      reward.reserved.base += deltaBaseReward;
      reward.reserved.delegated += deltaDelegatedReward;
    } else {
      reward.reserved.base -= deltaBaseReward;
      reward.reserved.delegated -= deltaDelegatedReward;
    }
  }

  /// @notice Increase reserved staker rewards.
  /// @param baseRewardAmount The amount of base rewards to reserve
  /// or unreserve for
  /// @param delegatedRewardAmount The amount of delegated rewards to reserve
  /// or unreserve for
  function _reserve(
    Reward storage reward,
    uint256 baseRewardAmount,
    uint256 delegatedRewardAmount
  ) internal {
    _updateReservedRewards(
      reward,
      baseRewardAmount,
      delegatedRewardAmount,
      true
    );
  }

  /// @notice Decrease reserved staker rewards.
  /// @param baseRewardAmount The amount of base rewards to reserve
  /// or unreserve for
  /// @param delegatedRewardAmount The amount of delegated rewards to reserve
  /// or unreserve for
  function _unreserve(
    Reward storage reward,
    uint256 baseRewardAmount,
    uint256 delegatedRewardAmount
  ) internal {
    _updateReservedRewards(
      reward,
      baseRewardAmount,
      delegatedRewardAmount,
      false
    );
  }

  /// @notice function does multiple things:
  /// - Unreserves future staking rewards to make them available for withdrawal;
  /// - Expires the reward to stop rewards from accumulating;
  function _release(
    Reward storage reward,
    uint256 amount,
    uint256 delegatedAmount
  ) internal {
    // Accumulate base and delegation rewards before unreserving rewards to save gas costs.
    // We can use the accumulated reward per micro LINK and accumulated delegation reward
    // to simplify reward calculations in migrate() and unstake() instead of recalculating.
    _accumulateDelegationRewards(reward, delegatedAmount);
    _accumulateBaseRewards(reward);
    _unreserve(reward, amount - delegatedAmount, delegatedAmount);

    reward.endTimestamp = block.timestamp;
  }

  /// @notice calculates an amount that community stakers have to delegate to operators
  /// @param amount base staked amount to calculate delegated amount against
  /// @param delegationRateDenominator Delegation rate used to calculate delegated stake amount
  function _getDelegatedAmount(
    uint256 amount,
    uint256 delegationRateDenominator
  ) internal pure returns (uint256) {
    return amount / delegationRateDenominator;
  }

  /// @notice calculates the amount of stake that remains after accounting for delegation requirement
  /// @param amount base staked amount to calculate non-delegated amount against
  /// @param delegationRateDenominator Delegation rate used to calculate delegated stake amount
  function _getNonDelegatedAmount(
    uint256 amount,
    uint256 delegationRateDenominator
  ) internal pure returns (uint256) {
    return amount - _getDelegatedAmount(amount, delegationRateDenominator);
  }

  /// @return uint256 the remaining reward duration (time until end), or 0 if expired/ended.
  function _getRemainingDuration(Reward storage reward)
    internal
    view
    returns (uint256)
  {
    return _isDepleted(reward) ? 0 : reward.endTimestamp - block.timestamp;
  }

  /// @notice This function is called when the staking pool is initialized,
  /// pool size is changed, reward rates are changed, rewards are added, and an alert is raised
  /// @param maxPoolSize Current maximum staking pool size
  /// @param totalStakedAmount Currently staked amount across community stakers and operators
  /// @param newRate New reward rate if it needs to be changed
  /// @param minRewardDuration The minimum duration rewards need to last for
  /// @param availableReward available reward amount
  /// @param totalDelegatedAmount total delegated amount delegated by community stakers
  function _updateDuration(
    Reward storage reward,
    uint256 maxPoolSize,
    uint256 totalStakedAmount,
    uint256 newRate,
    uint256 minRewardDuration,
    uint256 availableReward,
    uint256 totalDelegatedAmount
  ) internal {
    uint256 earnedBaseRewards = _getEarnedBaseRewards(
      reward,
      totalStakedAmount,
      totalDelegatedAmount
    );
    uint256 earnedDelegationRewards = _getEarnedDelegationRewards(
      reward,
      totalDelegatedAmount
    );

    uint256 remainingRewards = availableReward -
      earnedBaseRewards -
      earnedDelegationRewards;

    if (newRate != uint256(reward.base.rate)) {
      reward.base.rate = newRate._toUint80();
    }

    uint256 availableRewardDuration = (remainingRewards * REWARD_PRECISION) /
      (newRate * maxPoolSize);

    // Validate that the new reward duration is at least the min reward duration.
    // This is a safety mechanism to guard against operational mistakes.
    if (availableRewardDuration < minRewardDuration)
      revert RewardDurationTooShort();

    // Because we utilize unreserved rewards we need to update reserved amounts as well.
    // Reserved amounts are set to currently earned rewards plus new future rewards
    // based on the available reward duration.
    reward.reserved.base = (earnedBaseRewards +
      // Future base rewards for currently staked amounts based on the new duration
      _calculateReward(
        reward,
        totalStakedAmount - totalDelegatedAmount,
        availableRewardDuration
      ))._toUint96();

    reward.reserved.delegated = (earnedDelegationRewards +
      // Future delegation rewards for currently staked amounts based on the new duration
      _calculateReward(reward, totalDelegatedAmount, availableRewardDuration))
      ._toUint96();

    reward.endTimestamp = block.timestamp + availableRewardDuration;
  }

  /// @return The total amount of base rewards earned by all stakers
  function _getEarnedBaseRewards(
    Reward storage reward,
    uint256 totalStakedAmount,
    uint256 totalDelegatedAmount
  ) internal view returns (uint256) {
    return
      reward.reserved.base -
      _calculateReward(
        reward,
        totalStakedAmount - totalDelegatedAmount,
        _getRemainingDuration(reward)
      );
  }

  /// @return The total amount of delegated rewards earned by all node operators
  function _getEarnedDelegationRewards(
    Reward storage reward,
    uint256 totalDelegatedAmount
  ) internal view returns (uint256) {
    return
      reward.reserved.delegated -
      _calculateReward(
        reward,
        totalDelegatedAmount,
        _getRemainingDuration(reward)
      );
  }

  /// @notice Slashes all on feed node operators.
  /// Node operators are slashed the minimum of either the
  /// amount of rewards they have earned or the amount
  /// of rewards earned by the minimum operator stake amount
  /// over the slashable duration.
  function _slashOnFeedOperators(
    Reward storage reward,
    uint256 minOperatorStakeAmount,
    uint256 slashableDuration,
    address[] memory feedOperators,
    mapping(address => StakingPoolLib.Staker) storage stakers,
    uint256 totalDelegatedAmount
  ) internal {
    if (reward.delegated.delegatesCount == 0) return; // Skip slashing if there are no staking operators

    uint256 slashableBaseRewards = _getSlashableBaseRewards(
      reward,
      minOperatorStakeAmount,
      slashableDuration
    );
    uint256 slashableDelegatedRewards = _getSlashableDelegatedRewards(
      reward,
      slashableDuration,
      totalDelegatedAmount
    );

    uint256 totalSlashedBaseReward;
    uint256 totalSlashedDelegatedReward;

    uint256[] memory slashedBaseAmounts = new uint256[](feedOperators.length);
    uint256[] memory slashedDelegatedAmounts = new uint256[](
      feedOperators.length
    );

    for (uint256 i; i < feedOperators.length; i++) {
      address operator = feedOperators[i];
      uint256 operatorStakedAmount = stakers[operator].stakedAmount;
      if (operatorStakedAmount == 0) continue;
      slashedBaseAmounts[i] = _slashOperatorBaseRewards(
        reward,
        slashableBaseRewards,
        operator,
        operatorStakedAmount
      );
      slashedDelegatedAmounts[i] = _slashOperatorDelegatedRewards(
        reward,
        slashableDelegatedRewards,
        operator,
        totalDelegatedAmount
      );
      totalSlashedBaseReward += slashedBaseAmounts[i];
      totalSlashedDelegatedReward += slashedDelegatedAmounts[i];
    }
    reward.reserved.base -= totalSlashedBaseReward._toUint96();
    reward.reserved.delegated -= totalSlashedDelegatedReward._toUint96();

    emit RewardSlashed(
      feedOperators,
      slashedBaseAmounts,
      slashedDelegatedAmounts
    );
  }

  /// @return The amount of base rewards to slash
  /// @notice The amount of rewards accrued over the slashable duration for a
  /// minimum node operator stake amount
  function _getSlashableBaseRewards(
    Reward storage reward,
    uint256 minOperatorStakeAmount,
    uint256 slashableDuration
  ) private view returns (uint256) {
    return _calculateReward(reward, minOperatorStakeAmount, slashableDuration);
  }

  /// @return The amount of delegated rewards to slash
  /// @dev The amount of delegated rewards accrued over the slashable duration
  function _getSlashableDelegatedRewards(
    Reward storage reward,
    uint256 slashableDuration,
    uint256 totalDelegatedAmount
  ) private view returns (uint256) {
    DelegatedRewards memory delegatedRewards = reward.delegated;

    return
      _calculateReward(reward, totalDelegatedAmount, slashableDuration) /
      // We don't validate for delegatedRewards.delegatesCount to be a
      // non-zero value as this is already checked in _slashOnFeedOperators.
      uint256(delegatedRewards.delegatesCount);
  }

  /// @notice Slashes an on feed node operator the minimum of
  /// either the total amount of base rewards they have
  /// earned or the amount of rewards earned by the
  ///  minimum operator stake amount over the slashable duration.
  function _slashOperatorBaseRewards(
    Reward storage reward,
    uint256 slashableRewards,
    address operator,
    uint256 operatorStakedAmount
  ) private returns (uint256) {
    uint256 earnedRewards = _getOperatorEarnedBaseRewards(
      reward,
      operator,
      operatorStakedAmount
    );
    uint256 slashedRewards = Math.min(slashableRewards, earnedRewards); // max capped by earnings
    reward.missed[operator].base += slashedRewards._toUint96();
    return slashedRewards;
  }

  /// @notice Slashes an on feed node operator the minimum of
  /// either the total amount of delegated rewards they have
  /// earned or the amount of delegated rewards they have
  /// earned over the slashable duration.
  function _slashOperatorDelegatedRewards(
    Reward storage reward,
    uint256 slashableRewards,
    address operator,
    uint256 totalDelegatedAmount
  ) private returns (uint256) {
    uint256 earnedRewards = _getOperatorEarnedDelegatedRewards(
      reward,
      operator,
      totalDelegatedAmount
    );
    uint256 slashedRewards = Math.min(slashableRewards, earnedRewards); // max capped by earnings
    reward.missed[operator].delegated += slashedRewards._toUint96();
    return slashedRewards;
  }

  /// @return The amount of base rewards an operator
  /// has earned.
  function _getOperatorEarnedBaseRewards(
    Reward storage reward,
    address operator,
    uint256 operatorStakedAmount
  ) internal view returns (uint256) {
    return
      _calculateAccruedBaseRewards(reward, operatorStakedAmount) -
      uint256(reward.missed[operator].base);
  }

  /// @return The amount of delegated rewards an operator
  /// has earned.
  function _getOperatorEarnedDelegatedRewards(
    Reward storage reward,
    address operator,
    uint256 totalDelegatedAmount
  ) internal view returns (uint256) {
    return
      _calculateAccruedDelegatedRewards(reward, totalDelegatedAmount) -
      uint256(reward.missed[operator].delegated);
  }

  /// @return The current timestamp or, if the current timestamp has passed reward
  /// end timestamp, reward end timestamp.
  /// @dev This is necessary to ensure that rewards are calculated correctly
  /// after the reward is depleted.
  function _getCappedTimestamp(Reward storage reward)
    internal
    view
    returns (uint256)
  {
    return Math.min(uint256(reward.endTimestamp), block.timestamp);
  }
}

File 3 of 21 : IStakingOwner.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

/// @notice Owner functions restricted to the setup and maintenance
/// of the staking contract by the owner.
interface IStakingOwner {
  /// @notice This error is thrown when an zero delegation rate is supplied
  error InvalidDelegationRate();

  /// @notice This error is thrown when an invalid regular period threshold is supplied
  error InvalidRegularPeriodThreshold();

  /// @notice This error is thrown when an invalid min operator stake amount is
  /// supplied
  error InvalidMinOperatorStakeAmount();

  /// @notice This error is thrown when an invalid min community stake amount
  /// is supplied
  error InvalidMinCommunityStakeAmount();

  /// @notice This error is thrown when an invalid max alerting reward is
  /// supplied
  error InvalidMaxAlertingRewardAmount();

  /// @notice This error is thrown when the pool is started with an empty
  /// merkle root
  error MerkleRootNotSet();

  /// @notice Adds one or more operators to a list of operators
  /// @dev Should only callable by the Owner
  /// @param operators A list of operator addresses to add
  function addOperators(address[] calldata operators) external;

  /// @notice Removes one or more operators from a list of operators. When an
  /// operator is removed, we store their principal in a separate mapping to
  /// prevent immediate withdrawals. This is so that the removed operator can
  /// only unstake at the same time as every other staker.
  /// @dev Should only be callable by the owner when the pool is open.
  /// When an operator is removed they can stake as a community staker.
  /// We allow that because the alternative (checking for removed stake before
  /// staking) is going to unnecessarily increase gas costs in 99.99% of the
  /// cases.
  /// @param operators A list of operator addresses to remove
  function removeOperators(address[] calldata operators) external;

  /// @notice Allows the contract owner to set the list of on-feed operator addresses who are subject to slashing
  /// @dev Existing feed operators are cleared before setting the new operators.
  /// @param operators New list of on-feed operator staker addresses
  function setFeedOperators(address[] calldata operators) external;

  /// @return List of the ETH-USD feed node operators' staking addresses
  function getFeedOperators() external view returns (address[] memory);

  /// @notice This function can be called to change the reward rate for the pool.
  /// This change only affects future rewards, i.e. rewards earned at a previous
  /// rate are unaffected.
  /// @dev Should only be callable by the owner. The rate can be increased or decreased.
  /// The new rate cannot be 0.
  /// @param rate The new reward rate
  function changeRewardRate(uint256 rate) external;

  /// @notice This function can be called to add rewards to the pool
  /// @dev Should only be callable by the owner
  /// @param amount The amount of rewards to add to the pool
  function addReward(uint256 amount) external;

  /// @notice This function can be called to withdraw unused reward amount from
  /// the staking pool. It can be called before the pool is initialized, after
  /// the pool is concluded or when the reward expires.
  /// @dev Should only be callable by the owner when the pool is closed
  function withdrawUnusedReward() external;

  /// @notice Set the pool config
  /// @param maxPoolSize The max amount of staked LINK allowed in the pool
  /// @param maxCommunityStakeAmount The max amount of LINK a community staker can stake
  /// @param maxOperatorStakeAmount The max amount of LINK a Node Op can stake
  function setPoolConfig(
    uint256 maxPoolSize,
    uint256 maxCommunityStakeAmount,
    uint256 maxOperatorStakeAmount
  ) external;

  /// @notice Transfers LINK tokens and initializes the reward
  /// @dev Uses ERC20 approve + transferFrom flow
  /// @param amount rewards amount in LINK
  /// @param initialRewardRate The amount of LINK earned per second for
  /// each LINK staked.
  function start(uint256 amount, uint256 initialRewardRate) external;

  /// @notice Closes the pool, unreserving future staker rewards, expires the
  /// reward and releases unreserved rewards
  function conclude() external;

  /// @notice This function pauses staking
  /// @dev Sets the pause flag to true
  function emergencyPause() external;

  /// @notice This function unpauses staking
  /// @dev Sets the pause flag to false
  function emergencyUnpause() external;
}

File 4 of 21 : StakingPoolLib.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

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

library StakingPoolLib {
  using SafeCast for uint256;

  /// @notice This event is emitted when the staking pool is opened for stakers
  event PoolOpened();
  /// @notice This event is emitted when the staking pool is concluded
  event PoolConcluded();
  /// @notice This event is emitted when the staking pool's maximum size is
  /// increased
  /// @param maxPoolSize the new maximum pool size
  event PoolSizeIncreased(uint256 maxPoolSize);
  /// @notice This event is emitted when the maximum stake amount
  // for community stakers is increased
  /// @param maxStakeAmount the new maximum stake amount
  event MaxCommunityStakeAmountIncreased(uint256 maxStakeAmount);
  /// @notice This event is emitted when the maximum stake amount for node
  /// operators is increased
  /// @param maxStakeAmount the new maximum stake amount
  event MaxOperatorStakeAmountIncreased(uint256 maxStakeAmount);
  /// @notice This event is emitted when an operator is added
  /// @param operator address of the operator that was added to the staking pool
  event OperatorAdded(address operator);
  /// @notice This event is emitted when an operator is removed
  /// @param operator address of the operator that was removed from the staking pool
  /// @param amount principal amount that will be available for withdrawal when staking ends
  event OperatorRemoved(address operator, uint256 amount);
  /// @notice This event is emitted when the contract owner sets the list
  /// of feed operators subject to slashing
  /// @param feedOperators new list of feed operator staking addresses
  event FeedOperatorsSet(address[] feedOperators);
  /// @notice Surfaces the required pool status to perform an operation
  /// @param currentStatus current status of the pool (true if open / false if closed)
  /// @param requiredStatus required status of the pool to proceed
  /// (true if pool must be open / false if pool must be closed)
  error InvalidPoolStatus(bool currentStatus, bool requiredStatus);
  /// @notice This error is raised when attempting to decrease maximum pool size
  /// @param maxPoolSize the current maximum pool size
  error InvalidPoolSize(uint256 maxPoolSize);
  /// @notice This error is raised when attempting to decrease maximum stake amount
  /// for community stakers or node operators
  /// @param maxStakeAmount the current maximum stake amount
  error InvalidMaxStakeAmount(uint256 maxStakeAmount);
  /// @notice This error is raised when attempting to add more node operators without
  /// sufficient available pool space to reserve their allocations.
  /// @param remainingPoolSize the remaining pool space available to reserve
  /// @param requiredPoolSize the required reserved pool space to add new node operators
  error InsufficientRemainingPoolSpace(
    uint256 remainingPoolSize,
    uint256 requiredPoolSize
  );
  /// @param requiredAmount minimum required stake amount
  error InsufficientStakeAmount(uint256 requiredAmount);
  /// @notice This error is raised when stakers attempt to stake past pool limits
  /// @param remainingAmount maximum remaining amount that can be staked. This is
  /// the difference between the existing staked amount and the individual and global limits.
  error ExcessiveStakeAmount(uint256 remainingAmount);
  /// @notice This error is raised when stakers attempt to exit the pool
  /// @param staker address of the staker who attempted to withdraw funds
  error StakeNotFound(address staker);
  /// @notice This error is raised when addresses with existing stake is added as an operator
  /// @param staker address of the staker who is being added as an operator
  error ExistingStakeFound(address staker);
  /// @notice This error is raised when an address is duplicated in the supplied list of operators.
  /// This can happen in addOperators and setFeedOperators functions.
  /// @param operator address of the operator
  error OperatorAlreadyExists(address operator);
  /// @notice This error is thrown when the owner attempts to remove an on-feed operator.
  /// @dev The owner must remove the operator from the on-feed list first.
  error OperatorIsAssignedToFeed(address operator);
  /// @notice This error is raised when removing an operator in removeOperators
  /// and setFeedOperators
  /// @param operator address of the operator
  error OperatorDoesNotExist(address operator);
  /// @notice This error is raised when operator has been removed from the pool
  /// and is attempted to be readded
  /// @param operator address of the locked operator
  error OperatorIsLocked(address operator);
  /// @notice This error is raised when attempting to start staking with less
  /// than the minimum required node operators
  /// @param currentOperatorsCount The current number of operators in the staking pool
  /// @param minInitialOperatorsCount The minimum required number of operators
  /// in the staking pool before opening
  error InadequateInitialOperatorsCount(
    uint256 currentOperatorsCount,
    uint256 minInitialOperatorsCount
  );

  struct PoolLimits {
    // The max amount of staked LINK allowed in the pool
    uint96 maxPoolSize;
    // The max amount of LINK a community staker can stake
    uint80 maxCommunityStakeAmount;
    // The max amount of LINK a Node Op can stake
    uint80 maxOperatorStakeAmount;
  }

  struct PoolState {
    // Flag that signals if the staking pool is open for staking
    bool isOpen;
    // Total number of operators added to the staking pool
    uint8 operatorsCount;
    // Total amount of LINK staked by community stakers
    uint96 totalCommunityStakedAmount;
    // Total amount of LINK staked by operators
    uint96 totalOperatorStakedAmount;
  }

  struct Staker {
    // Flag that signals whether a staker is an operator
    bool isOperator;
    // Flag that signals whether a staker is an on-feed operator
    bool isFeedOperator;
    // Amount of LINK staked by a staker
    uint96 stakedAmount;
    // Amount of LINK staked by a removed operator that can be withdrawn
    // Removed operators can only withdraw at the end of staking.
    // Used to know which operators have been removed.
    uint96 removedStakeAmount;
  }

  struct Pool {
    mapping(address => Staker) stakers;
    address[] feedOperators;
    PoolState state;
    PoolLimits limits;
    // Sum of removed operator principals that have not been withdrawn.
    // Used to make sure that contract's balance is correct.
    // total staked amount + total removed amount + available rewards = current balance
    uint256 totalOperatorRemovedAmount;
  }

  /// @notice Sets staking pool parameters
  /// @param maxPoolSize Maximum total stake amount across all stakers
  /// @param maxCommunityStakeAmount Maximum stake amount for a single community staker
  /// @param maxOperatorStakeAmount Maximum stake amount for a single node operator
  function _setConfig(
    Pool storage pool,
    uint256 maxPoolSize,
    uint256 maxCommunityStakeAmount,
    uint256 maxOperatorStakeAmount
  ) internal {
    if (maxOperatorStakeAmount > maxPoolSize)
      revert InvalidMaxStakeAmount(maxOperatorStakeAmount);

    if (pool.limits.maxPoolSize > maxPoolSize)
      revert InvalidPoolSize(maxPoolSize);
    if (pool.limits.maxCommunityStakeAmount > maxCommunityStakeAmount)
      revert InvalidMaxStakeAmount(maxCommunityStakeAmount);
    if (pool.limits.maxOperatorStakeAmount > maxOperatorStakeAmount)
      revert InvalidMaxStakeAmount(maxOperatorStakeAmount);

    PoolState memory poolState = pool.state;
    if (
      maxPoolSize <
      (poolState.operatorsCount * maxOperatorStakeAmount) +
        poolState.totalCommunityStakedAmount
    ) revert InvalidMaxStakeAmount(maxOperatorStakeAmount);
    if (pool.limits.maxPoolSize != maxPoolSize) {
      pool.limits.maxPoolSize = maxPoolSize._toUint96();
      emit PoolSizeIncreased(maxPoolSize);
    }
    if (pool.limits.maxCommunityStakeAmount != maxCommunityStakeAmount) {
      pool.limits.maxCommunityStakeAmount = maxCommunityStakeAmount._toUint80();
      emit MaxCommunityStakeAmountIncreased(maxCommunityStakeAmount);
    }
    if (pool.limits.maxOperatorStakeAmount != maxOperatorStakeAmount) {
      pool.limits.maxOperatorStakeAmount = maxOperatorStakeAmount._toUint80();
      emit MaxOperatorStakeAmountIncreased(maxOperatorStakeAmount);
    }
  }

  /// @notice Opens the staking pool
  function _open(Pool storage pool, uint256 minInitialOperatorCount) internal {
    if (uint256(pool.state.operatorsCount) < minInitialOperatorCount)
      revert InadequateInitialOperatorsCount(
        pool.state.operatorsCount,
        minInitialOperatorCount
      );
    pool.state.isOpen = true;
    emit PoolOpened();
  }

  /// @notice Closes the staking pool
  function _close(Pool storage pool) internal {
    pool.state.isOpen = false;
    emit PoolConcluded();
  }

  /// @notice Returns true if a supplied staker address is in the operators list
  /// @param staker Address of a staker
  /// @return bool
  function _isOperator(Pool storage pool, address staker)
    internal
    view
    returns (bool)
  {
    return pool.stakers[staker].isOperator;
  }

  /// @notice Returns the sum of all principal staked in the pool
  /// @return totalStakedAmount
  function _getTotalStakedAmount(Pool storage pool)
    internal
    view
    returns (uint256)
  {
    StakingPoolLib.PoolState memory poolState = pool.state;
    return
      uint256(poolState.totalCommunityStakedAmount) +
      uint256(poolState.totalOperatorStakedAmount);
  }

  /// @notice Returns the amount of remaining space available in the pool for
  /// community stakers. Community stakers can only stake up to this amount
  /// even if they are within their individual limits.
  /// @return remainingPoolSpace
  function _getRemainingPoolSpace(Pool storage pool)
    internal
    view
    returns (uint256)
  {
    StakingPoolLib.PoolState memory poolState = pool.state;
    return
      uint256(pool.limits.maxPoolSize) -
      (uint256(poolState.operatorsCount) *
        uint256(pool.limits.maxOperatorStakeAmount)) -
      uint256(poolState.totalCommunityStakedAmount);
  }

  /// @dev Required conditions for adding operators:
  /// - Operators can only been added to the pool if they have no prior stake.
  /// - Operators can only been readded to the pool if they have no removed
  /// stake.
  /// - Operators cannot be added to the pool after staking ends (either through
  /// conclusion or through reward expiry).
  function _addOperators(Pool storage pool, address[] calldata operators)
    internal
  {
    uint256 requiredReservedPoolSpace = operators.length *
      uint256(pool.limits.maxOperatorStakeAmount);
    uint256 remainingPoolSpace = _getRemainingPoolSpace(pool);
    if (requiredReservedPoolSpace > remainingPoolSpace)
      revert InsufficientRemainingPoolSpace(
        remainingPoolSpace,
        requiredReservedPoolSpace
      );

    for (uint256 i; i < operators.length; i++) {
      if (pool.stakers[operators[i]].isOperator)
        revert OperatorAlreadyExists(operators[i]);
      if (pool.stakers[operators[i]].stakedAmount > 0)
        revert ExistingStakeFound(operators[i]);
      // Avoid edge-cases where we attempt to add an operator that has
      // locked principal (this means that the operator was previously removed).
      if (pool.stakers[operators[i]].removedStakeAmount > 0)
        revert OperatorIsLocked(operators[i]);
      pool.stakers[operators[i]].isOperator = true;
      emit OperatorAdded(operators[i]);
    }

    // Safely update operators count with respect to the maximum of 255 operators
    pool.state.operatorsCount =
      pool.state.operatorsCount +
      operators.length._toUint8();
  }

  /// @notice Helper function to set the list of on-feed Operator addresses
  /// @param operators List of Operator addresses
  function _setFeedOperators(Pool storage pool, address[] calldata operators)
    internal
  {
    for (uint256 i; i < pool.feedOperators.length; i++) {
      delete pool.stakers[pool.feedOperators[i]].isFeedOperator;
    }
    delete pool.feedOperators;

    for (uint256 i; i < operators.length; i++) {
      address newFeedOperator = operators[i];
      if (!_isOperator(pool, newFeedOperator))
        revert OperatorDoesNotExist(newFeedOperator);
      if (pool.stakers[newFeedOperator].isFeedOperator)
        revert OperatorAlreadyExists(newFeedOperator);

      pool.stakers[newFeedOperator].isFeedOperator = true;
    }
    pool.feedOperators = operators;

    emit FeedOperatorsSet(operators);
  }
}

File 5 of 21 : IAlertsController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

interface IAlertsController {
  /// @param alerter The address of an alerter
  /// @param roundId The feed's round ID that an alert has been raised for
  /// @param rewardAmount The amount of LINK rewarded to the alerter
  /// @notice Emitted when a valid alert is raised for a feed round
  event AlertRaised(address alerter, uint256 roundId, uint256 rewardAmount);

  /// @param roundId The feed's round ID that the alerter is trying to raise an alert for
  /// @notice This error is thrown when an alerter tries to raise an
  // alert for a round that has already been alerted.
  error AlertAlreadyExists(uint256 roundId);

  /// @notice This error is thrown when alerting conditions are not met and the
  /// alert is invalid.
  error AlertInvalid();

  /// @notice This function creates an alert for a stalled feed
  function raiseAlert() external;

  /// @notice This function checks to see whether the alerter may raise an alert
  /// to claim rewards
  function canAlert(address alerter) external view returns (bool);
}

File 6 of 21 : IMigratable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

interface IMigratable {
  /// @notice This event is emitted when a migration target is proposed by the contract owner.
  /// @param migrationTarget Contract address to migrate stakes to.
  event MigrationTargetProposed(address migrationTarget);
  /// @notice This event is emitted after a 7 day period has passed since a migration target is proposed, and the target is accepted.
  /// @param migrationTarget Contract address to migrate stakes to.
  event MigrationTargetAccepted(address migrationTarget);
  /// @notice This event is emitted when a staker migrates their stake to the migration target.
  /// @param staker Staker address
  /// @param principal Principal amount deposited
  /// @param baseReward Amount of base rewards withdrawn
  /// @param delegationReward Amount of delegation rewards withdrawn (if applicable)
  /// @param data Migration payload
  event Migrated(
    address staker,
    uint256 principal,
    uint256 baseReward,
    uint256 delegationReward,
    bytes data
  );

  /// @notice This error is raised when the contract owner supplies a non-contract migration target.
  error InvalidMigrationTarget();

  /// @notice This function returns the migration target contract address
  function getMigrationTarget() external view returns (address);

  /// @notice This function allows the contract owner to set a proposed
  /// migration target address. If the migration target is valid it renounces
  /// the previously accepted migration target (if any).
  /// @param migrationTarget Contract address to migrate stakes to.
  function proposeMigrationTarget(address migrationTarget) external;

  /// @notice This function allows the contract owner to accept a proposed migration target address after a waiting period.
  function acceptMigrationTarget() external;

  /// @notice This function allows stakers to migrate funds to a new staking pool.
  /// @param data Migration path details
  function migrate(bytes calldata data) external;
}

File 7 of 21 : IStaking.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

interface IStaking {
  /// @notice This event is emitted when a staker adds stake to the pool.
  /// @param staker Staker address
  /// @param newStake New principal amount staked
  /// @param totalStake Total principal amount staked
  event Staked(address staker, uint256 newStake, uint256 totalStake);
  /// @notice This event is emitted when a staker exits the pool.
  /// @param staker Staker address
  /// @param principal Principal amount staked
  /// @param baseReward base reward earned
  /// @param delegationReward delegation reward earned, if any
  event Unstaked(
    address staker,
    uint256 principal,
    uint256 baseReward,
    uint256 delegationReward
  );

  /// @notice This error is thrown whenever the sender is not the LINK token
  error SenderNotLinkToken();

  /// @notice This error is thrown whenever an address does not have access
  /// to successfully execute a transaction
  error AccessForbidden();

  /// @notice This error is thrown whenever a zero-address is supplied when
  /// a non-zero address is required
  error InvalidZeroAddress();

  /// @notice This function allows stakers to exit the pool after it has been
  /// concluded. It returns the principal as well as base and delegation
  /// rewards.
  function unstake() external;

  /// @notice This function allows removed operators to withdraw their original
  /// principal. Operators can only withdraw after the pool is closed, like
  /// every other staker.
  function withdrawRemovedStake() external;

  /// @return address LINK token contract's address that is used by the pool
  function getChainlinkToken() external view returns (address);

  /// @param staker address
  /// @return uint256 staker's staked principal amount
  function getStake(address staker) external view returns (uint256);

  /// @notice Returns true if an address is an operator
  function isOperator(address staker) external view returns (bool);

  /// @notice The staking pool starts closed and only allows
  /// stakers to stake once it's opened
  /// @return bool pool status
  function isActive() external view returns (bool);

  /// @return uint256 current maximum staking pool size
  function getMaxPoolSize() external view returns (uint256);

  /// @return uint256 minimum amount that can be staked by a community staker
  /// @return uint256 maximum amount that can be staked by a community staker
  function getCommunityStakerLimits() external view returns (uint256, uint256);

  /// @return uint256 minimum amount that can be staked by an operator
  /// @return uint256 maximum amount that can be staked by an operator
  function getOperatorLimits() external view returns (uint256, uint256);

  /// @return uint256 reward initialization timestamp
  /// @return uint256 reward expiry timestamp
  function getRewardTimestamps() external view returns (uint256, uint256);

  /// @return uint256 current reward rate, expressed in juels per second per micro LINK
  function getRewardRate() external view returns (uint256);

  /// @return uint256 current delegation rate
  function getDelegationRateDenominator() external view returns (uint256);

  /// @return uint256 total amount of LINK tokens made available for rewards in
  /// Juels
  /// @dev This reflects how many rewards were made available over the
  /// lifetime of the staking pool. This is not updated when the rewards are
  /// unstaked or migrated by the stakers. This means that the contract balance
  /// will dip below available amount when the reward expires and users start
  /// moving their rewards.
  function getAvailableReward() external view returns (uint256);

  /// @return uint256 amount of base rewards earned by a staker in Juels
  function getBaseReward(address) external view returns (uint256);

  /// @return uint256 amount of delegation rewards earned by an operator in Juels
  function getDelegationReward(address) external view returns (uint256);

  /// @notice Total delegated amount is calculated by dividing the total
  /// community staker staked amount by the delegation rate, i.e.
  /// totalDelegatedAmount = pool.totalCommunityStakedAmount / delegationRateDenominator
  /// @return uint256 staked amount that is used when calculating delegation rewards in Juels
  function getTotalDelegatedAmount() external view returns (uint256);

  /// @notice Delegates count increases after an operator is added to the list
  /// of operators and stakes the minimum required amount.
  /// @return uint256 number of staking operators that are eligible for delegation rewards
  function getDelegatesCount() external view returns (uint256);

  /// @return uint256 total amount of base rewards earned by all stakers in Juels
  function getEarnedBaseRewards() external view returns (uint256);

  /// @return uint256 total amount of delegated rewards earned by all node operators in Juels
  function getEarnedDelegationRewards() external view returns (uint256);

  /// @return uint256 total amount staked by community stakers and operators in Juels
  function getTotalStakedAmount() external view returns (uint256);

  /// @return uint256 total amount staked by community stakers in Juels
  function getTotalCommunityStakedAmount() external view returns (uint256);

  /// @return uint256 the sum of removed operator principals that have not been
  /// withdrawn from the staking pool in Juels.
  /// @dev Used to make sure that contract's balance is correct.
  /// total staked amount + total removed amount + available rewards = current balance
  function getTotalRemovedAmount() external view returns (uint256);

  /// @notice This function returns the pause state
  /// @return bool whether or not the pool is paused
  function isPaused() external view returns (bool);

  /// @return address The address of the feed being monitored to raise alerts for
  function getMonitoredFeed() external view returns (address);
}

File 8 of 21 : IMerkleAccessController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

interface IMerkleAccessController {
  /// @notice Emitted when the contract owner updates the staking allowlist
  /// @param newMerkleRoot The root of a new Staking allowlist merkle tree
  event MerkleRootChanged(bytes32 newMerkleRoot);

  /// @notice Validates if a community staker has access to the private staking pool
  /// @param staker The community staker's address
  /// @param proof Merkle proof for the community staker's allowlist
  function hasAccess(address staker, bytes32[] calldata proof)
    external
    view
    returns (bool);

  /// @notice This function is called to update the staking allowlist in a private staking pool
  /// @dev Only callable by the contract owner
  /// @param newMerkleRoot Merkle Tree root, used to prove access for community stakers
  /// will be required at start but can be removed at any time by the owner when
  /// staking access will be granted to the public.
  function setMerkleRoot(bytes32 newMerkleRoot) external;

  /// @return The current root of the Staking allowlist merkle tree
  function getMerkleRoot() external view returns (bytes32);
}

File 9 of 21 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 10 of 21 : TypeAndVersionInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract TypeAndVersionInterface {
  function typeAndVersion() external pure virtual returns (string memory);
}

File 11 of 21 : ConfirmedOwner.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./ConfirmedOwnerWithProposal.sol";

/**
 * @title The ConfirmedOwner contract
 * @notice A contract with helpers for basic contract ownership.
 */
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
  constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}

File 12 of 21 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";

File 13 of 21 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

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

  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

File 14 of 21 : LinkTokenInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface LinkTokenInterface {
  function allowance(address owner, address spender) external view returns (uint256 remaining);

  function approve(address spender, uint256 value) external returns (bool success);

  function balanceOf(address owner) external view returns (uint256 balance);

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

  function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);

  function increaseApproval(address spender, uint256 subtractedValue) external;

  function name() external view returns (string memory tokenName);

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

  function totalSupply() external view returns (uint256 totalTokensIssued);

  function transfer(address to, uint256 value) external returns (bool success);

  function transferAndCall(
    address to,
    uint256 value,
    bytes calldata data
  ) external returns (bool success);

  function transferFrom(
    address from,
    address to,
    uint256 value
  ) external returns (bool success);
}

File 15 of 21 : MerkleProof.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Trees proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];
            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = _efficientHash(computedHash, proofElement);
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = _efficientHash(proofElement, computedHash);
            }
        }
        return computedHash;
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 16 of 21 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }
}

File 17 of 21 : SafeCast.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

library SafeCast {
  error CastError();

  /// @notice This is used to safely case timestamps to uint8
  uint256 private constant MAX_UINT_8 = type(uint8).max;
  /// @notice This is used to safely case timestamps to uint32
  uint256 private constant MAX_UINT_32 = type(uint32).max;
  /// @notice This is used to safely case timestamps to uint80
  uint256 private constant MAX_UINT_80 = type(uint80).max;
  /// @notice This is used to safely case timestamps to uint96
  uint256 private constant MAX_UINT_96 = type(uint96).max;

  function _toUint8(uint256 value) internal pure returns (uint8) {
    if (value > MAX_UINT_8) revert CastError();
    return uint8(value);
  }

  function _toUint32(uint256 value) internal pure returns (uint32) {
    if (value > MAX_UINT_32) revert CastError();
    return uint32(value);
  }

  function _toUint80(uint256 value) internal pure returns (uint80) {
    if (value > MAX_UINT_80) revert CastError();
    return uint80(value);
  }

  function _toUint96(uint256 value) internal pure returns (uint96) {
    if (value > MAX_UINT_96) revert CastError();
    return uint96(value);
  }
}

File 18 of 21 : 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 19 of 21 : ConfirmedOwnerWithProposal.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./interfaces/OwnableInterface.sol";

/**
 * @title The ConfirmedOwner contract
 * @notice A contract with helpers for basic contract ownership.
 */
contract ConfirmedOwnerWithProposal is OwnableInterface {
  address private s_owner;
  address private s_pendingOwner;

  event OwnershipTransferRequested(address indexed from, address indexed to);
  event OwnershipTransferred(address indexed from, address indexed to);

  constructor(address newOwner, address pendingOwner) {
    require(newOwner != address(0), "Cannot set owner to zero");

    s_owner = newOwner;
    if (pendingOwner != address(0)) {
      _transferOwnership(pendingOwner);
    }
  }

  /**
   * @notice Allows an owner to begin transferring ownership to a new address,
   * pending.
   */
  function transferOwnership(address to) public override onlyOwner {
    _transferOwnership(to);
  }

  /**
   * @notice Allows an ownership transfer to be completed by the recipient.
   */
  function acceptOwnership() external override {
    require(msg.sender == s_pendingOwner, "Must be proposed owner");

    address oldOwner = s_owner;
    s_owner = msg.sender;
    s_pendingOwner = address(0);

    emit OwnershipTransferred(oldOwner, msg.sender);
  }

  /**
   * @notice Get the current owner
   */
  function owner() public view override returns (address) {
    return s_owner;
  }

  /**
   * @notice validate, transfer ownership, and emit relevant events
   */
  function _transferOwnership(address to) private {
    require(to != msg.sender, "Cannot transfer to self");

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /**
   * @notice validate access
   */
  function _validateOwnership() internal view {
    require(msg.sender == s_owner, "Only callable by owner");
  }

  /**
   * @notice Reverts if called by anyone other than the contract owner.
   */
  modifier onlyOwner() {
    _validateOwnership();
    _;
  }
}

File 20 of 21 : OwnableInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface OwnableInterface {
  function owner() external returns (address);

  function transferOwnership(address recipient) external;

  function acceptOwnership() external;
}

File 21 of 21 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"contract LinkTokenInterface","name":"LINKAddress","type":"address"},{"internalType":"contract AggregatorV3Interface","name":"monitoredFeed","type":"address"},{"internalType":"uint256","name":"initialMaxPoolSize","type":"uint256"},{"internalType":"uint256","name":"initialMaxCommunityStakeAmount","type":"uint256"},{"internalType":"uint256","name":"initialMaxOperatorStakeAmount","type":"uint256"},{"internalType":"uint256","name":"minCommunityStakeAmount","type":"uint256"},{"internalType":"uint256","name":"minOperatorStakeAmount","type":"uint256"},{"internalType":"uint256","name":"priorityPeriodThreshold","type":"uint256"},{"internalType":"uint256","name":"regularPeriodThreshold","type":"uint256"},{"internalType":"uint256","name":"maxAlertingRewardAmount","type":"uint256"},{"internalType":"uint256","name":"minInitialOperatorCount","type":"uint256"},{"internalType":"uint256","name":"minRewardDuration","type":"uint256"},{"internalType":"uint256","name":"slashableDuration","type":"uint256"},{"internalType":"uint256","name":"delegationRateDenominator","type":"uint256"}],"internalType":"struct Staking.PoolConstructorParams","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessForbidden","type":"error"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"AlertAlreadyExists","type":"error"},{"inputs":[],"name":"AlertInvalid","type":"error"},{"inputs":[],"name":"CastError","type":"error"},{"inputs":[{"internalType":"uint256","name":"remainingAmount","type":"uint256"}],"name":"ExcessiveStakeAmount","type":"error"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"ExistingStakeFound","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentOperatorsCount","type":"uint256"},{"internalType":"uint256","name":"minInitialOperatorsCount","type":"uint256"}],"name":"InadequateInitialOperatorsCount","type":"error"},{"inputs":[{"internalType":"uint256","name":"remainingPoolSize","type":"uint256"},{"internalType":"uint256","name":"requiredPoolSize","type":"uint256"}],"name":"InsufficientRemainingPoolSpace","type":"error"},{"inputs":[{"internalType":"uint256","name":"requiredAmount","type":"uint256"}],"name":"InsufficientStakeAmount","type":"error"},{"inputs":[],"name":"InvalidDelegationRate","type":"error"},{"inputs":[],"name":"InvalidMaxAlertingRewardAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxStakeAmount","type":"uint256"}],"name":"InvalidMaxStakeAmount","type":"error"},{"inputs":[],"name":"InvalidMigrationTarget","type":"error"},{"inputs":[],"name":"InvalidMinCommunityStakeAmount","type":"error"},{"inputs":[],"name":"InvalidMinOperatorStakeAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxPoolSize","type":"uint256"}],"name":"InvalidPoolSize","type":"error"},{"inputs":[{"internalType":"bool","name":"currentStatus","type":"bool"},{"internalType":"bool","name":"requiredStatus","type":"bool"}],"name":"InvalidPoolStatus","type":"error"},{"inputs":[],"name":"InvalidRegularPeriodThreshold","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"MerkleRootNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorAlreadyExists","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorDoesNotExist","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorIsAssignedToFeed","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorIsLocked","type":"error"},{"inputs":[],"name":"RewardDurationTooShort","type":"error"},{"inputs":[],"name":"SenderNotLinkToken","type":"error"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"StakeNotFound","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"alerter","type":"address"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"name":"AlertRaised","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"newMerkleRoot","type":"bytes32"}],"name":"MerkleRootChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegationReward","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Migrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"migrationTarget","type":"address"}],"name":"MigrationTargetAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"migrationTarget","type":"address"}],"name":"MigrationTargetProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"newStake","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalStake","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegationReward","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[],"name":"acceptMigrationTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"addOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"alerter","type":"address"}],"name":"canAlert","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newRate","type":"uint256"}],"name":"changeRewardRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"conclude","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyUnpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAvailableReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getBaseReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainlinkToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCommunityStakerLimits","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDelegatesCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDelegationRateDenominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getDelegationReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEarnedBaseRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEarnedDelegationRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeedOperators","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxPoolSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMigrationTarget","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMonitoredFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOperatorLimits","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTimestamps","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalCommunityStakedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalDelegatedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalRemovedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalStakedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"hasAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"migrationTarget","type":"address"}],"name":"proposeMigrationTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"raiseAlert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"removeOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"setFeedOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newMerkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxPoolSize","type":"uint256"},{"internalType":"uint256","name":"maxCommunityStakeAmount","type":"uint256"},{"internalType":"uint256","name":"maxOperatorStakeAmount","type":"uint256"}],"name":"setPoolConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"initialRewardRate","type":"uint256"}],"name":"start","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawRemovedStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawUnusedReward","outputs":[],"stateMutability":"nonpayable","type":"function"}]

34620006c557620063ff388190036101e0601f8201601f19168101906001600160401b03821190821017620005b7576101c09282916040526101e03912620006c5576040516101c081016001600160401b03811182821017620005b7576040526101e0516001600160a01b0381168103620006c5578152610200516001600160a01b0381168103620006c55760208201526102205160408201526102405160608201526102605160808201526102805160a08201526102a05160c08201526102c05160e08201526102e05161010082015261030051610120820152610320516101408201526103405161016082015261036051610180820152610380516101a082015233156200068057600080546001600160a01b031916331790556001805460ff60a01b1916905580516001600160a01b0316156200066e5760208101516001600160a01b0316156200066e576101a08101511562000646576101a08101518015620006585764e8d4a5100006620006465761010081015160e08201511015620006345760c081015115620006225760c0810151608082015110620006225760a08101516060820151106200061057610120810151608082015110620005fe576040810151906060810151608082015190838211620005cd57600554936001600160601b0385168110620005e657606085901c6001600160501b03168210620005cd57828560b01c1162000588576040516001600160401b036080820190811190821117620005b757608081016040526004549060ff82161515815260ff8260081c169081602082015260606040820193600180831b038160101c168552600180831b039060701c169101528060001904851181151516620005a15790516001600160601b0316908402908101908110620005a157811062000588576001600160601b03851681900362000516575b506005546101a09450606081901c6001600160501b0316829003620004b4575b5050600554818160b01c036200045b575b505060018060a01b0381511660805260018060a01b0360208201511660a05260e081015160c05261010081015160e0526101208101516101005260c08101516101205260a0810151610140526101408101516101605261016081015161018052610180810151825201516101c052604051615d1f9081620006e082396080518181816104eb01528181610798015281816110700152818161169c01528181611ba801528181612c2501528181614b690152614e05015260a05181818161148401528181612ad701526149f1015260c051818181612b260152614a40015260e051818181612b570152614a7101526101005181818161597801526159a10152610120518181816114eb01528181612c8a0152615455015261014051818181610451015261510a01526101605181611b3e015261018051818181611116015281816112070152611c1301526101a05181612c6901526101c051818181610c3e01528181614c7001528181614dc30152818161519b015261584b0152f35b7f816587cb2e773af4f3689a03d7520fabff3462605ded374b485b13994c0d7b52916020916001600160b01b03196200049483620006ca565b60b01b169060018060b01b031617600555604051908152a13880620002e0565b7fb5f554e5ef00806bace1edbb84186512ebcefa2af7706085143f501f29314df791602091600160601b600160b01b03620004ef83620006ca565b60601b16600160601b600160b01b03199190911617600555604051908152a13880620002cf565b6001600160601b03811162000576576001600160601b03199094166001600160601b038516176005556040519384526101a0937f7f4f497e086b2eb55f8a9885ba00d33399bbe0ebcb92ea092834386435a1b9c090602090a138620002af565b60405163408ba96f60e11b8152600490fd5b60405163bc91aa3360e01b815260048101849052602490fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b60405163bc91aa3360e01b815260048101839052602490fd5b60249060405190630f9e1c3b60e11b82526004820152fd5b60405163a9be3a0960e01b8152600490fd5b60405163941b857f60e01b8152600490fd5b6040516384dada8560e01b8152600490fd5b6040516310919fb960e11b8152600490fd5b60405163027953ef60e61b8152600490fd5b634e487b7160e01b600052601260045260246000fd5b60405163f6b2911f60e01b8152600490fd5b60405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f00000000000000006044820152606490fd5b600080fd5b6001600160501b03908181116200057657169056fe60806040526004361015610013575b600080fd5b60003560e01c8063049b2ca0146104065780630641bdd8146103fd5780630fbc8f5b146103f4578063165d35e1146103eb578063181f5a77146103e25780631a9d4c7c146103d95780631ddb5552146103d057806322f3e2d4146103c75780632def6620146103be57806332e28850146103b557806338adb6f0146103ac57806349590657146103a35780634a4e3bd51461039a57806351858e271461039157806359f01879146103885780635aa6e0131461037f5780635c975abb1461029e5780635e8b40d7146103765780635fec60f81461036d57806363b2c85a146103645780636d70f7ae1461035b578063741040021461035257806374de4ec41461034957806374f237c41461034057806379ba5097146103375780637a7664601461032e5780637cb64759146103255780637e1a37861461031c5780638019e7d01461031357806383db28a01461030a57806387e900b1146103015780638856398f146102f85780638932a90d146102ef5780638a44f337146102e65780638da5cb5b146102dd5780638fb4b573146102d45780639a109bc2146102cb5780639d0a3864146102c2578063a07aea1c146102b9578063a4c0ed36146102b0578063a7a2f5aa146102a7578063b187bd261461029e578063bfbd9b1b14610295578063c1852f581461028c578063d365a37714610283578063da9c732f1461027a578063e0974ea514610271578063e5f9297314610268578063e937fdaa1461025f578063ebdb56f3146102565763f2fde38b1461024e57600080fd5b61000e613173565b5061000e6130e1565b5061000e612fe2565b5061000e612e50565b5061000e612e34565b5061000e612a3f565b5061000e61255d565b5061000e612535565b5061000e6122e6565b5061000e610bfe565b5061000e6122ca565b5061000e612244565b5061000e611e8f565b5061000e611d96565b5061000e611c91565b5061000e611b17565b5061000e611ae2565b5061000e611728565b5061000e61151a565b5061000e6114d0565b5061000e6114a8565b5061000e611456565b5061000e611437565b5061000e61140c565b5061000e6113bf565b5061000e611362565b5061000e611235565b5061000e611176565b5061000e610fe4565b5061000e610f9f565b5061000e610f4e565b5061000e610cfa565b5061000e610cab565b5061000e610c25565b5061000e610a3d565b5061000e610a0c565b5061000e610977565b5061000e61089f565b5061000e610880565b5061000e610864565b5061000e610842565b5061000e6106c4565b5061000e61069e565b5061000e610669565b5061000e6105cc565b5061000e61056d565b5061000e6104bd565b5061000e610490565b5061000e610436565b503461000e57600060031936011261000e5760206bffffffffffffffffffffffff60045460101c16604051908152f35b503461000e57600060031936011261000e57600554604080517f0000000000000000000000000000000000000000000000000000000000000000815260609290921c69ffffffffffffffffffff16602083015290f35b0390f35b503461000e57600060031936011261000e5760206bffffffffffffffffffffffff60055416604051908152f35b503461000e57600060031936011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b919082519283825260005b8481106105595750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161051a565b503461000e57600060031936011261000e5761048c60405161058e81611d12565b600d81527f5374616b696e6720302e312e3000000000000000000000000000000000000000602082015260405191829160208352602083019061050f565b503461000e57600060031936011261000e5760206105e8615ab9565b64e8d4a5100061063c6105f9614dad565b926bffffffffffffffffffffffff600a541693810390811161065c575b61063761062161393a565b9169ffffffffffffffffffff60095416906134e6565b6134e6565b04810390811161064f575b604051908152f35b6106576133cf565b610647565b6106646133cf565b610616565b503461000e57600060031936011261000e57602073ffffffffffffffffffffffffffffffffffffffff600f5416604051908152f35b503461000e57600060031936011261000e5760206106ba614b09565b6040519015158152f35b503461000e5760008060031936011261083f576106df614b09565b6108075761077e602061073e6107396106f733615648565b92917f204fccf0d92ed8d48f204adb39b2e81e92bad0dedb93f5716ca9478cfb57de0060806040513381528389820152846040820152866060820152a161356d565b61356d565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915291829081906044820190565b03818573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af180156107fa575b6107cc575b50604051f35b6107ec9060203d81116107f3575b6107e48183611d2e565b8101906139da565b50386107c6565b503d6107da565b6108026139f2565b6107c1565b604490604051907fa30a70c2000000000000000000000000000000000000000000000000000000008252600160048301526024820152fd5b80fd5b503461000e57600060031936011261000e57602060ff60085416604051908152f35b503461000e57600060031936011261000e576020610647615ab9565b503461000e57600060031936011261000e576020601154604051908152f35b503461000e57600060031936011261000e576108b96132a6565b60015460ff8160a01c1615610919577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff166001557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b503461000e57600060031936011261000e576109916132a6565b740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff6001546109db60ff8260a01c161561409e565b16176001557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b503461000e57600060031936011261000e57600c54600b546040805163ffffffff9093168352602083019190915290f35b503461000e5760008060031936011261083f57610a58614b09565b61080757610ab2610aa1610a8c3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460701c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff1690565b8015610bc957602081610ad2610acd61077e9460065461348b565b600655565b610b27610aff3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff8154169055565b7f204fccf0d92ed8d48f204adb39b2e81e92bad0dedb93f5716ca9478cfb57de0060405180610b86843383606090600092949373ffffffffffffffffffffffffffffffffffffffff608083019616825260208201528260408201520152565b0390a16040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915291829081906044820190565b6040517fe4adde72000000000000000000000000000000000000000000000000000000008152336004820152602490fd5b0390fd5b503461000e57600060031936011261000e57602060ff60015460a01c166040519015158152f35b503461000e57600060031936011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b90815180825260208080930193019160005b828110610c81575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610c73565b503461000e57600060031936011261000e5761048c610cc8614103565b604051918291602083526020830190610c61565b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e57602060031936011261000e57600435610d1881610cdc565b610d206132a6565b803b158015610f2f575b8015610f0b575b8015610ee7575b8015610e39575b610e0f57610e0a7f5c74c441be501340b2713817a6c6975e6f3d4a4ae39fa1ac0bf75d3c54a0cad391610d957fffffffffffffffffffffffff0000000000000000000000000000000000000000600f5416600f55565b610dda8173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600d541617600d55565b610de342600e55565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390a1005b60046040517f367a1038000000000000000000000000000000000000000000000000000000008152fd5b506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527fa4c0ed3600000000000000000000000000000000000000000000000000000000600482015260208160248173ffffffffffffffffffffffffffffffffffffffff86165afa908115610eda575b600091610ebc575b5015610d3f565b610ed4915060203d81116107f3576107e48183611d2e565b38610eb5565b610ee26139f2565b610ead565b50600f5473ffffffffffffffffffffffffffffffffffffffff828116911614610d38565b50600d5473ffffffffffffffffffffffffffffffffffffffff828116911614610d31565b503073ffffffffffffffffffffffffffffffffffffffff821614610d2a565b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff600435610f8181610cdc565b166000526002602052602060ff604060002054166040519015158152f35b503461000e57600060031936011261000e576020610fbb614dad565b64e8d4a5100061063c6bffffffffffffffffffffffff600a5460601c169261063761062161393a565b503461000e57602060031936011261000e577fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d610e0a6004356110256132a6565b61102d6159c3565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810182905260208160648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af18015611169575b61114b575b5061113b6110d1610aa16005546bffffffffffffffffffffffff1690565b6110d9615ab9565b906111016110f260095469ffffffffffffffffffff1690565b69ffffffffffffffffffff1690565b9061110a614b20565b91611113614dad565b937f00000000000000000000000000000000000000000000000000000000000000009261357a565b6040519081529081906020820190565b6111629060203d81116107f3576107e48183611d2e565b50386110b3565b6111716139f2565b6110ae565b503461000e57602060031936011261000e576004356111936132a6565b61119b6159c3565b801561000e5760207f1e3be2efa25bca5bff2215c7b30b31086e703d6aa7d9b9a1f8ba62c5291219ad9161122c6111d0614dad565b6111d981613c86565b6111e1613bb5565b6bffffffffffffffffffffffff600554166111fa615ab9565b9084611204614b20565b927f00000000000000000000000000000000000000000000000000000000000000009261357a565b604051908152a1005b503461000e5760008060031936011261083f5773ffffffffffffffffffffffffffffffffffffffff8060015416330361130457815473ffffffffffffffffffffffffffffffffffffffff16600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556112d77fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b604051913391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08484a3f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152fd5b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff60043561139581610cdc565b16600052600260205260206bffffffffffffffffffffffff60406000205460101c16604051908152f35b503461000e57602060031936011261000e577f1b930366dfeaa7eb3b325021e4ae81e36527063452ee55b86c95f85b36f4c31c60206004356113ff6132a6565b80601155604051908152a1005b503461000e57600060031936011261000e57602069ffffffffffffffffffff60095416604051908152f35b503461000e57600060031936011261000e576020600654604051908152f35b503461000e57600060031936011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57602060031936011261000e5760206106476004356114cb81610cdc565b614d24565b503461000e57600060031936011261000e57600554604080517f0000000000000000000000000000000000000000000000000000000000000000815260b09290921c602083015290f35b503461000e57602060031936011261000e5767ffffffffffffffff60043581811161000e573660238201121561000e57806004013591821161000e576024810190602483369201011161000e5761156f614b09565b6116f05773ffffffffffffffffffffffffffffffffffffffff91826115a9600f5473ffffffffffffffffffffffffffffffffffffffff1690565b1615610e0f576116989260209260006107396116626116236115ca33615648565b9194907f667838b33bdc898470de09e0e746990f2adc11b965b7fe6828e502ebc39e0434604051806116018d8c88878d33876141c5565b0390a1600f5473ffffffffffffffffffffffffffffffffffffffff169561356d565b93611636604051978892338b8501614204565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101875286611d2e565b604051968795869485937f4000aea000000000000000000000000000000000000000000000000000000000855260048501614231565b03927f0000000000000000000000000000000000000000000000000000000000000000165af180156116e3575b6116cc575b005b6116ca9060203d81116107f3576107e48183611d2e565b6116eb6139f2565b6116c5565b60446040517fa30a70c20000000000000000000000000000000000000000000000000000000081526001600482015260006024820152fd5b503461000e57606060031936011261000e576044356004803560243561174c6132a6565b6117546159c3565b818411611aaa576005546bffffffffffffffffffffffff811690838211611a75578269ffffffffffffffffffff8260601c1611611a4057859060b01c11611a075761179d6159d2565b936117df6117bc876106376117b660208a015160ff1690565b60ff1690565b6117d9610aa16040809901516bffffffffffffffffffffffff1690565b9061356d565b84106119d35750938291611839936000960361195e575b806118136110f260055469ffffffffffffffffffff9060601c1690565b036118cd575b508061182a6110f260055460b01c90565b0361183c575b506110d9615ab9565b51f35b6118c4816118b561186d7f816587cb2e773af4f3689a03d7520fabff3462605ded374b485b13994c0d7b5294613980565b75ffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffff000000000000000000000000000000000000000000006005549260b01b16911617600555565b85519081529081906020820190565b0390a138611830565b611955816119466118fe7fb5f554e5ef00806bace1edbb84186512ebcefa2af7706085143f501f29314df794613980565b7fffffffffffffffffffff00000000000000000000ffffffffffffffffffffffff75ffffffffffffffffffff0000000000000000000000006005549260601b16911617600555565b86519081529081906020820190565b0390a138611819565b6119a361196a846139c1565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006005541617600555565b84518381527f7f4f497e086b2eb55f8a9885ba00d33399bbe0ebcb92ea092834386435a1b9c090602090a16117f6565b84517fbc91aa3300000000000000000000000000000000000000000000000000000000815290810186815281906020010390fd5b50506040517fbc91aa33000000000000000000000000000000000000000000000000000000008152918201928352509081906020010390fd5b6040517fbc91aa3300000000000000000000000000000000000000000000000000000000815280860184815281906020010390fd5b6040517f1f3c387600000000000000000000000000000000000000000000000000000000815280860185815281906020010390fd5b50506040517fbc91aa330000000000000000000000000000000000000000000000000000000081529081019182529081906020010390fd5b503461000e57600060031936011261000e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b503461000e57604060031936011261000e57611b316132a6565b60115415611c6757611b627f0000000000000000000000000000000000000000000000000000000000000000615a1b565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048083019190915230602483015235604482015260208160648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af18015611c5a575b611c3c575b6116ca611c08610aa16005546bffffffffffffffffffffffff1690565b611c10614b20565b907f000000000000000000000000000000000000000000000000000000000000000090602435906139ff565b611c539060203d81116107f3576107e48183611d2e565b5038611beb565b611c626139f2565b611be6565b60046040517f9f8a28f2000000000000000000000000000000000000000000000000000000008152fd5b503461000e57602060031936011261000e576020610647600435611cb481610cdc565b614c08565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117611d0557604052565b611d0d611cb9565b604052565b6040810190811067ffffffffffffffff821117611d0557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611d0557604052565b60209067ffffffffffffffff8111611d89575b60051b0190565b611d91611cb9565b611d82565b503461000e57604060031936011261000e57600435611db481610cdc565b6024359067ffffffffffffffff821161000e573660238301121561000e578160040135611de081611d6f565b92611dee6040519485611d2e565b81845260209160248386019160051b8301019136831161000e57602401905b828210611e335761048c611e218787613325565b60405190151581529081906020820190565b81358152908301908301611e0d565b90602060031983011261000e5760043567ffffffffffffffff9283821161000e578060238301121561000e57816004013593841161000e5760248460051b8301011161000e576024019190565b503461000e57611e9e36611e42565b90611ea76132a6565b63ffffffff600c54161515806121ec575b6121b457611ed4611ece6110f260055460b01c90565b836134e6565b611edc615ae7565b80821161217957505060005b828110611f49576116ca611f1584611f0f611f0960045460ff9060081c1690565b91614090565b90615401565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff006004549260081b16911617600455565b611f8f611f88611f62611f5d848787613fdf565b613ff7565b73ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460ff1690565b61211c57611fbf610aa1611faa611f62611f5d858888613fdf565b5460101c6bffffffffffffffffffffffff1690565b6120be57611fda610aa1610a8c611f62611f5d858888613fdf565b6120605780612021611ff6611f62611f5d61205b958888613fdf565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b7fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d612053610de3611f5d848888613fdf565b0390a16133ff565b611ee8565b611f5d90610bfa9361207193613fdf565b6040517f7a378b9c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b611f5d90610bfa936120cf93613fdf565b6040517f602d4d1100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b611f5d90610bfa9361212d93613fdf565b6040517ea5216600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b6040517f35cf446b00000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b60446040517fa30a70c20000000000000000000000000000000000000000000000000000000081526000600482015260016024820152fd5b506121f5614b09565b15611eb8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff8111612237575b01160190565b61223f611cb9565b612231565b503461000e57606060031936011261000e5760043561226281610cdc565b6044359067ffffffffffffffff821161000e573660238301121561000e5781600401359061228f826121fb565b9161229d6040519384611d2e565b808352366024828601011161000e5760208160009260246116ca9701838701378401015260243590614ded565b503461000e57600060031936011261000e576020610647614dad565b503461000e576122f536611e42565b6122fd6132a6565b60005b6003548110156123725780612368612340611f6261232061236d95615b42565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff8154169055565b6133ff565b612300565b5061237b615b87565b60005b8181106123c15750816123b2827f40aed8e423b39a56b445ae160f4c071fc2cfb48ee0b6dcd5ffeb6bc5b18d10d094615bda565b610e0a60405192839283615cb7565b6123cf611f5d828486613fdf565b6124036123ff611f888373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b1590565b6124ec5761243e6124348273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460081c60ff1690565b6124a4579061236861247361249f9373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b6101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff825416179055565b61237e565b6040517ea5216600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b6040517feac13dcd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e57602060031936011261000e5760206106ba60043561255881610cdc565b61496a565b503461000e5761256c36611e42565b906125756132a6565b61257d6159c3565b61258d612588614dad565b613c86565b60005b8281106125b4576116ca611f156125a685614090565b60045460081c60ff1661407c565b6125c2611f5d828585613fdf565b6125f46125ef8273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b614001565b906126026123ff8351151590565b6124ec5760208201516129f6576126df92917f2360404a74478febece1a14f11275f22ada88d19ef96f7d785913010bfff447991612053612656610aa16040809501516bffffffffffffffffffffffff1690565b92836126e4575b6126b261268a8473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055565b51928392836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b612590565b6127f66127ac6126fb6126f686614c08565b6139c1565b61275861271f600a9261271a84546bffffffffffffffffffffffff1690565b613dcf565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a541617600a55565b61278761271f6127726126f661276c61393a565b8b6138d2565b83546bffffffffffffffffffffffff16613dcf565b61271a6127966126f688614d24565b915460601c6bffffffffffffffffffffffff1690565b7fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b61283b61280d61280860085460ff1690565b61404b565b60ff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006008541617600855565b6128906128688473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff8154169055565b61299c610acd6bffffffffffffffffffffffff6128ac876139c1565b61291a6128ce8261271a6004546bffffffffffffffffffffffff9060701c1690565b7fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff79ffffffffffffffffffffffff00000000000000000000000000006004549260701b16911617600455565b612993816129488973ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b907fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff79ffffffffffffffffffffffff000000000000000000000000000083549260701b169116179055565b1660065461356d565b6129f16129c98473ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008154169055565b61265d565b6040517fded6031900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e5760008060031936011261083f57612a5a6159c3565b612a8a610aa1611faa3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b8015612e0a5760409081517ffeaf968c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9160a082600481867f0000000000000000000000000000000000000000000000000000000000000000165afa918215612dfd575b85908693612dbd575b5069ffffffffffffffffffff16916010548314612d8c57612b4b7f00000000000000000000000000000000000000000000000000000000000000008261356d565b4210612d2d57612b7c907f00000000000000000000000000000000000000000000000000000000000000009061356d565b4210908180612d56575b612d2d5792827fd2720e8f454493f612cc97499fe8cbce7fa4d4c18d346fe7104e9042df1c1edd612bc6612c21948997612bc1602098601055565b61596b565b875133815260208101939093526040830181905291606090a185517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915293849283919082906044820190565b03927f0000000000000000000000000000000000000000000000000000000000000000165af18015612d20575b612d02575b50612cae612c5f614dad565b612c67614103565b7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000614366565b611839612ccb610aa16005546bffffffffffffffffffffffff1690565b612cd3615ab9565b612ceb6110f260095469ffffffffffffffffffff1690565b612cf3614b20565b91612cfc614dad565b936136c4565b612d199060203d81116107f3576107e48183611d2e565b5038612c53565b612d286139f2565b612c4e565b600485517ffc53c50a000000000000000000000000000000000000000000000000000000008152fd5b50612d876123ff611f883373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b612b86565b84517ff3553c2200000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b69ffffffffffffffffffff9350612deb915060a03d8111612df6575b612de38183611d2e565b81019061427a565b50949150612b0a9050565b503d612dd9565b612e056139f2565b612b01565b60046040517fef67f5d8000000000000000000000000000000000000000000000000000000008152fd5b503461000e57600060031936011261000e576020610647614b20565b503461000e57600060031936011261000e57612e6a6132a6565b612e726159c3565b612e7a615ab9565b612f437fffffffffffffffff00000000000000000000000000000000000000000000000077ffffffffffffffffffffffff000000000000000000000000612f73612f27612f21612f1b612ecb614dad565b97612ed589613c86565b612edd613bb5565b888103908111612fd5575b610637612ef361393a565b9169ffffffffffffffffffff60095416948591612f1b8561063764e8d4a51000998a946134e6565b046139c1565b9a6134e6565b600a54956bffffffffffffffffffffffff958691828916613dcf565b1694857fffffffffffffffffffffffffffffffffffffffff00000000000000000000000088161760601c16613dcf565b60601b1692161717600a5542600b557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006004541660045560006040517ff7d0e0f15586495da8c687328ead30fb829d9da55538cb0ef73dd229e517cdb88282a1f35b612fdd6133cf565b612ee8565b503461000e57600060031936011261000e57612ffc6132a6565b600d5473ffffffffffffffffffffffffffffffffffffffff168015610e0f57613026600e5461355d565b4210612e0a576130719073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600f541617600f55565b61309e7fffffffffffffffffffffffff0000000000000000000000000000000000000000600d5416600d55565b7ffa33c052bbee754f3c0482a89962daffe749191fa33c696a61e947fbfd68bd84610e0a610de3600f5473ffffffffffffffffffffffffffffffffffffffff1690565b503461000e5760008060031936011261083f576130fc6132a6565b613104614b09565b6108075761077e6020613141613118614b20565b600a54906131366bffffffffffffffffffffffff918284169061348b565b9160601c169061348b565b6040518181527f150a6ec0e6f4e9ddcaaaa1674f157d91165a42d60653016f87a9fc870a39f050908060208101610b86565b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff6004356131a681610cdc565b6131ae6132a6565b1633811461324857806000917fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015561321d613204835473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b90604051917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788484a3f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff6000541633036132c757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152fd5b90601154801561336c576133699260405173ffffffffffffffffffffffffffffffffffffffff60208201921682526020815261336081611d12565b51902091613374565b90565b505050600190565b929091906000915b84518310156133c75761338f8386613469565b51908181116133b2576000526020526133ac6040600020926133ff565b9161337c565b906000526020526133ac6040600020926133ff565b915092501490565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461342d570190565b6134356133cf565b0190565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602091815181101561347e575b60051b010190565b613486613439565b613476565b9190820391821161349857565b6134a06133cf565b565b64e8d4a5100090807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166134da570290565b6134e26133cf565b0290565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166134da570290565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115613551570490565b613559613517565b0490565b9062093a80820180921161349857565b9190820180921161349857565b929394916135df906135d961358f85856138f5565b956135d36135b061359f88613957565b8b8a829d039081116136b75761348b565b6135c86110f260095469ffffffffffffffffffff1690565b850361366f576134a2565b926134e6565b90613547565b938410613645576136409464e8d4a51000613633866106378661361e61271f6126f661363a9b6117d9876136196127ac9e6126f69e61348b565b6138d2565b69ffffffffffffffffffff60095416906134e6565b049061356d565b4261356d565b600b55565b60046040517fda056d00000000000000000000000000000000000000000000000000000000008152fd5b6136b261367b86613980565b69ffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000006009541617600955565b6134a2565b6136bf6133cf565b61348b565b928491926136d283856138f5565b906137156136df85613957565b96838503948511613826575b878503948511613819575b6135d369ffffffffffffffffffff958660095416850361366f576134a2565b90811561380c575b0461372a8197829661348b565b90613734916138d2565b61373d9161356d565b613746906139c1565b613783906bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a541617600a55565b60095416613790916134e6565b9061379a916134e6565b64e8d4a5100090046137ab9161356d565b6137b4906139c1565b613802907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b613640904261356d565b613814613517565b61371d565b6138216133cf565b6136f6565b61382e6133cf565b6136eb565b6136bf93916135d9613899926135d3600a5469ffffffffffffffffffff61388c6bffffffffffffffffffffffff988984169b8c9a61386f61393a565b506009549560601c169a8b92600b544281116138b6575b5061348b565b9116850361366f576134a2565b918210613645576127ac61363a916126f661271f613640966139c1565b428103116138c5575b38613886565b6138cd6133cf565b6138bf565b64e8d4a51000916106376135599269ffffffffffffffffffff60095416906134e6565b61392564e8d4a51000916bffffffffffffffffffffffff600a541693810390811161065c5761063761062161393a565b0481039081116139325790565b6133696133cf565b600b5442811161394a5750600090565b4281039081116139325790565b64e8d4a510006139256bffffffffffffffffffffffff600a5460601c169261063761062161393a565b69ffffffffffffffffffff90818111613997571690565b60046040517f811752de000000000000000000000000000000000000000000000000000000008152fd5b6bffffffffffffffffffffffff90818111613997571690565b9081602091031261000e5751801515810361000e5790565b506040513d6000823e3d90fd5b600c5463ffffffff949385821661000e5783613b3f93613b3a7f125fc8494f786b470e3c39d0932a62e9e09e291ebd81ea19c57604f6d2b1d16798889569ffffffffffffffffffff613a5086613980565b167fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000060095416176009557fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000613aa442613b84565b928316911617600c556008547fffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffff70ffffffff000000000000000000000000008360681b169116176008557fffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffff79ffffffff000000000000000000000000000000000000000000006009549260b01b16911617600955565b613833565b613b7f613b51600c5463ffffffff1690565b600b5490604051948594859094939263ffffffff906060936080840197845260208401521660408201520152565b0390a1565b63ffffffff90818111613997571690565b9190916bffffffffffffffffffffffff8080941691160191821161349857565b613bc2600b544290613fcd565b600954907fffffffffffff00000000000000000000000000000000ffffffffffffffffffff79ffffffff00000000000000000000000000000000000000000000613c6c75ffffffffffffffffffffffff00000000000000000000613c62613c4b6126f663ffffffff8960b01c168803888111613c79575b69ffffffffffffffffffff8a166134e6565b6bffffffffffffffffffffffff8860501c16613b95565b60501b1693613b84565b60b01b1692161717600955565b613c816133cf565b613c39565b6126f6613c9291613cfd565b6008547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff6cffffffffffffffffffffffff0070ffffffff00000000000000000000000000613ceb613ce6600b544290613fcd565b613b84565b60681b169360081b1691161717600855565b61336990613d0d600b5442101590565b15613d82576bffffffffffffffffffffffff613d7864e8d4a51000613d6b613d4f600b54613d4963ffffffff60085460681c1663ffffffff1690565b9061348b565b6106376008549669ffffffffffffffffffff60095416906134e6565b046135d960ff8516613dbe565b9160081c1661356d565b6bffffffffffffffffffffffff613d7864e8d4a51000613d6b63ffffffff60085460681c16420342811115613d4f57613db96133cf565b613d4f565b60018110613dc95790565b50600190565b6bffffffffffffffffffffffff918216908216039190821161349857565b6001906bffffffffffffffffffffffff80911690811461342d570190565b6127ac906134a092613e1b61393a565b69ffffffffffffffffffff6009541691610637613e5984612f1b85610637613e528a612f1b64e8d4a510009a8b998a9889946134e6565b98876134e6565b9406613ecd575b06613eb8575b613ea7907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a54916bffffffffffffffffffffffff938491828516613b95565b1691161780600a5560601c16613b95565b90613ec5613ea791613ded565b919050613e66565b91613ed790613ded565b91613e60565b613f556134a09164e8d4a51000613f1181612f1b613ef961393a565b61063769ffffffffffffffffffff60095416876134e6565b9106613fbf575b600a547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006bffffffffffffffffffffffff80948194828516613b95565b1691161780600a5560601c16908111613fb2577fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b613fba6133cf565b6127ac565b613fc890613ded565b613f18565b9080821015613fda575090565b905090565b9190811015613fef5760051b0190565b611d91613439565b3561336981610cdc565b9060405161400e81611ce9565b606081935460ff81161515835260ff8160081c16151560208401526bffffffffffffffffffffffff90818160101c16604085015260701c16910152565b60ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9116019060ff821161349857565b9060ff8091169116039060ff821161349857565b60ff81116139975760ff1690565b156140a557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b60405190600354808352826020918282019060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b936000905b828210614156575050506134a092500383611d2e565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958895509381019390910190614140565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b919260a09373ffffffffffffffffffffffffffffffffffffffff6133699896931684526020840152604083015260608201528160808201520191614186565b60409073ffffffffffffffffffffffffffffffffffffffff61336995931681528160208201520191614186565b613369939273ffffffffffffffffffffffffffffffffffffffff6060931682526020820152816040820152019061050f565b519069ffffffffffffffffffff8216820361000e57565b908160a091031261000e5761428e81614263565b91602082015191604081015191613369608060608401519301614263565b906142b682611d6f565b6142c36040519182611d2e565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06142f18294611d6f565b0190602036910137565b90815180825260208080930193019160005b82811061431b575050505090565b83518552938101939281019260010161430d565b916143589061434a6133699593606086526060860190610c61565b9084820360208601526142fb565b9160408184039101526142fb565b939291909360ff61437960085460ff1690565b1615614522578264e8d4a510006143a6876106376143ad9569ffffffffffffffffffff60095416906134e6565b0495614529565b91600091826143bc82516142ac565b946143c783516142ac565b92855b81518710156144a1576143fa6143e08884613469565b5173ffffffffffffffffffffffffffffffffffffffff1690565b8861442b610aa1611faa8473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b8c811561449257898461447a946144578e61445161448b9a976144859a61445e986145b5565b92613469565b5288614723565b6144688b8a613469565b526144738a8c613469565b519061356d565b946144738988613469565b966133ff565b95926143ca565b50505050929561448b906133ff565b7e635ea9da6e262e92bb713d71840af7c567807ff35bf73e927490c6128324809899506144fd919650613b7f95506127ac9250926126f661271f6144e7614516966139c1565b600a546bffffffffffffffffffffffff16613dcf565b600a5460601c6bffffffffffffffffffffffff16613dcf565b6040519384938461432f565b5050509050565b64e8d4a5100061459960ff92610637604051956060870187811067ffffffffffffffff8211176145a8575b60405263ffffffff60085487811689526bffffffffffffffffffffffff8160081c1660208a015260681c16604088015269ffffffffffffffffffff60095416906134e6565b04915116908115613551570490565b6145b0611cb9565b614554565b916146b061467361336993946145cd600b5442101590565b156146f45761463761462c6145f0600b5463ffffffff60095460b01c169061348b565b955b61462661460e6009549869ffffffffffffffffffff8a166134e6565b6bffffffffffffffffffffffff809960501c1661356d565b906134e6565b64e8d4a51000900490565b90846146638873ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b541682039182116146e757613fcd565b936146a7614680866139c1565b9173ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b92835416613b95565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000825416179055565b6146ef6133cf565b613fcd565b61463761462c63ffffffff60095460b01c164203428111614716575b956145f2565b61471e6133cf565b614710565b916148066147ed613369939461473b600b5442101590565b1561484e576147ae61479664e8d4a5100061478961476d600b54613d4963ffffffff60085460681c1663ffffffff1690565b6106376008549969ffffffffffffffffffff60095416906134e6565b046135d960ff8816613dbe565b6bffffffffffffffffffffffff809660081c1661356d565b90846147da8873ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5460601c1682039182116146e757613fcd565b936147fa614680866139c1565b92835460601c16613b95565b7fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b6147ae61479664e8d4a5100061478963ffffffff60085460681c1642034281111561476d5761487b6133cf565b61476d565b600b546133699161462c914281116148f45763ffffffff60095460b01c1681039081116148e7575b905b6148d56009549269ffffffffffffffffffff6bffffffffffffffffffffffff8560501c1694166134e6565b8201809211156134e6576106376133cf565b6148ef6133cf565b6148a8565b5063ffffffff60095460b01c164203428111614911575b906148aa565b6149196133cf565b61490b565b9061493d73ffffffffffffffffffffffffffffffffffffffff91613cfd565b911660005260076020526bffffffffffffffffffffffff60406000205460601c1681039081116139325790565b61499a610aa1611faa8373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b15614b03576149aa6123ff614b09565b614b03576040517ffeaf968c00000000000000000000000000000000000000000000000000000000815260a08160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115614af6575b6000908192614ad1575b5069ffffffffffffffffffff601054911614614aca57614a657f00000000000000000000000000000000000000000000000000000000000000008261356d565b4210614aca57614a96907f00000000000000000000000000000000000000000000000000000000000000009061356d565b421015613dc957611f886133699173ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5050600090565b9050614aeb915060a03d8111612df657612de38183611d2e565b509291505038614a25565b614afe6139f2565b614a1b565b50600090565b60ff6004541680614b175790565b50600b54421090565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115614bfb575b600091614bca575b50614ba4615ab9565b8103908111614bbd575b60065481039081116139325790565b614bc56133cf565b614bae565b906020823d8211614bf3575b81614be360209383611d2e565b8101031261083f57505138614b9b565b3d9150614bd6565b614c036139f2565b614b93565b73ffffffffffffffffffffffffffffffffffffffff81169060009180835260026020526bffffffffffffffffffffffff80604085205460101c16938415614cf95760ff604082205416614cd357505050613d49610aa1614cc1614c9a614c9561336996613d497f000000000000000000000000000000000000000000000000000000000000000082613547565b614880565b9373ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b546bffffffffffffffffffffffff1690565b909250614ce260409294614880565b938152600760205220541681039081116139325790565b935050505090565b908015614d17575b810481039081116139325790565b614d1f613517565b614d09565b73ffffffffffffffffffffffffffffffffffffffff81166000526002602052604060002060405190614d5582611ce9565b549060ff821615908115815260ff8360081c161515602082015260606bffffffffffffffffffffffff808560101c169485604085015260701c16910152614aca5715614b035761336990614da7614dad565b9061491e565b6bffffffffffffffffffffffff60045460101c167f0000000000000000000000000000000000000000000000000000000000000000908115613551570490565b9173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169182330361502657614e3f60ff60015460a01c161561409e565b614e476159c3565b809264e8d4a51000808310614ff05782069182614f3f575b505050614e8f611f888473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b15614e9e57506134a091615415565b6011549081614eb3575b50506134a0916150ca565b805115612e0a57614f3491614ed4826020806123ff95518301019101615050565b90604051602081019061336081614f088a8591909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611d2e565b612e0a573880614ea8565b829450602091614f54600094614fae9361348b565b956040519485809481937fa9059cbb0000000000000000000000000000000000000000000000000000000083528b600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015614fe3575b614fc5575b8080614e5f565b614fdc9060203d81116107f3576107e48183611d2e565b5038614fbe565b614feb6139f2565b614fb9565b6040517f1d820b1700000000000000000000000000000000000000000000000000000000815264e8d4a510006004820152602490fd5b60046040517f4d695438000000000000000000000000000000000000000000000000000000008152fd5b602090818184031261000e5780519067ffffffffffffffff821161000e57019180601f8401121561000e57825161508681611d6f565b936150946040519586611d2e565b818552838086019260051b82010192831161000e578301905b8282106150bb575050505090565b815181529083019083016150ad565b91906150fc610aa1611faa8573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b92615107828561356d565b937f00000000000000000000000000000000000000000000000000000000000000008086106153bc575060055461514c9060601c69ffffffffffffffffffff166110f2565b80861161537857505061515d615ae7565b8083116153455750613b7f7f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909394615196612588614dad565b6152357f000000000000000000000000000000000000000000000000000000000000000061522f6151d06151ca8389613547565b8861348b565b916152296151e06126f685614880565b6146b061520d8a73ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b9161522483546bffffffffffffffffffffffff1690565b613b95565b87613547565b90613e0b565b61529d61525d615244866139c1565b60045460101c6bffffffffffffffffffffffff16613b95565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff6dffffffffffffffffffffffff00006004549260101b16911617600455565b6153126152a9826139c1565b6152d38573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b907fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff6dffffffffffffffffffffffff000083549260101b169116179055565b6040519384938460409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b6040517fb94339d80000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b610bfa916153859161348b565b6040519182917fb94339d8000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b6040517f1d820b170000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b60ff60019116019060ff821161349857565b9060ff8091169116019060ff821161349857565b9190615447610aa1611faa8573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b92615452828561356d565b937f00000000000000000000000000000000000000000000000000000000000000008086106153bc575061548b6110f260055460b01c90565b80861161537857507f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909394613b7f911561552d575b6154fc6154cf6126f686614880565b6146b061520d8673ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b6155246128ce61550b866139c1565b60045460701c6bffffffffffffffffffffffff16613b95565b61529d84613edd565b615538612588614dad565b61555361280d60085460ff81169081156155e2575b506153ef565b600880546155dd911c6bffffffffffffffffffffffff166155948573ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b6154c0565b6127ac6bffffffffffffffffffffffff6156159260081c1661271a600a546bffffffffffffffffffffffff9060601c1690565b6156427fffffffffffffffffffffffffffffffffffffff000000000000000000000000ff60085416600855565b3861554d565b6156756125ef8273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b9160408301926bffffffffffffffffffffffff90816156a086516bffffffffffffffffffffffff1690565b16156158cc576156c1906156b660045460ff1690565b615887575b51151590565b156157ed57506156fc6128ce6156e385516bffffffffffffffffffffffff1690565b60045460701c6bffffffffffffffffffffffff16613dcf565b6157e8610aa16157248461571f610aa188516bffffffffffffffffffffffff1690565b615913565b946157b861286861579161574c610aa16008546bffffffffffffffffffffffff9060081c1690565b613d49610aa161577c8b73ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5460601c6bffffffffffffffffffffffff1690565b9673ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b6157c761271f6144e7886139c1565b6157d66127ac6144fd876139c1565b516bffffffffffffffffffffffff1690565b929190565b836158716128686157918661571f610aa19661583961525d6158206158809b9c9d516bffffffffffffffffffffffff1690565b60045460101c6bffffffffffffffffffffffff16613dcf565b86516bffffffffffffffffffffffff167f00000000000000000000000000000000000000000000000000000000000000009116614d01565b6157d661271f6144e7876139c1565b9190600090565b615892612588614dad565b61589a613bb5565b6158c77fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060045416600455565b6156bb565b6040517fe4adde7200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602490fd5b73ffffffffffffffffffffffffffffffffffffffff64e8d4a5100061594d6bffffffffffffffffffffffff938460095460501c16906134e6565b04921660005260076020526040600020541681039081116139325790565b9061599e576133699060057f00000000000000000000000000000000000000000000000000000000000000009104613fcd565b507f000000000000000000000000000000000000000000000000000000000000000090565b6159cb614b09565b156121b457565b604051906159df82611ce9565b81606060045460ff81161515835260ff8160081c1660208401526bffffffffffffffffffffffff90818160101c16604085015260701c16910152565b6004549060ff8260081c1690808210615a825750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060019116176004557fded6ebf04e261e1eb2f3e3b268a2e6aee5b478c15b341eba5cf18b9bc80c2e636000604051a1565b60449250604051917fe709379900000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b615ac16159d2565b6bffffffffffffffffffffffff6060816040840151169201511681018091116139325790565b615aef6159d2565b6005549060406bffffffffffffffffffffffff91615b1c8385169460ff6020840151169060b01c906134e6565b8403938411615b35575b01511681039081116139325790565b615b3d6133cf565b615b26565b600354811015615b7a575b60036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b615b82613439565b615b4d565b60035460008060035581615b99575050565b600381527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b918201915b828110615bcf57505050565b818155600101615bc3565b67ffffffffffffffff8211615caa575b680100000000000000008211615c9d575b60035482600355808310615c5c575b50600360005260005b828110615c1f57505050565b60019060208335615c2f81610cdc565b930192817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b015501615c13565b827fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b91820191015b818110615c915750615c0a565b60008155600101615c84565b615ca5611cb9565b615bfb565b615cb2611cb9565b615bea565b90916040602092828482018583525201929160005b828110615cda575050505090565b90919293828060019273ffffffffffffffffffffffffffffffffffffffff8835615d0381610cdc565b16815201950193929101615ccc56fea164736f6c6343000810000a000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841900000000000000000000000000000000000000000014adf4b7320334b900000000000000000000000000000000000000000000000000017b7883c06916600000000000000000000000000000000000000000000000000a968163f0a57b4000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002ee000000000000000000000000000000000000000000000017b7883c0691660000000000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000278d00000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000000014

Deployed Bytecode

0x60806040526004361015610013575b600080fd5b60003560e01c8063049b2ca0146104065780630641bdd8146103fd5780630fbc8f5b146103f4578063165d35e1146103eb578063181f5a77146103e25780631a9d4c7c146103d95780631ddb5552146103d057806322f3e2d4146103c75780632def6620146103be57806332e28850146103b557806338adb6f0146103ac57806349590657146103a35780634a4e3bd51461039a57806351858e271461039157806359f01879146103885780635aa6e0131461037f5780635c975abb1461029e5780635e8b40d7146103765780635fec60f81461036d57806363b2c85a146103645780636d70f7ae1461035b578063741040021461035257806374de4ec41461034957806374f237c41461034057806379ba5097146103375780637a7664601461032e5780637cb64759146103255780637e1a37861461031c5780638019e7d01461031357806383db28a01461030a57806387e900b1146103015780638856398f146102f85780638932a90d146102ef5780638a44f337146102e65780638da5cb5b146102dd5780638fb4b573146102d45780639a109bc2146102cb5780639d0a3864146102c2578063a07aea1c146102b9578063a4c0ed36146102b0578063a7a2f5aa146102a7578063b187bd261461029e578063bfbd9b1b14610295578063c1852f581461028c578063d365a37714610283578063da9c732f1461027a578063e0974ea514610271578063e5f9297314610268578063e937fdaa1461025f578063ebdb56f3146102565763f2fde38b1461024e57600080fd5b61000e613173565b5061000e6130e1565b5061000e612fe2565b5061000e612e50565b5061000e612e34565b5061000e612a3f565b5061000e61255d565b5061000e612535565b5061000e6122e6565b5061000e610bfe565b5061000e6122ca565b5061000e612244565b5061000e611e8f565b5061000e611d96565b5061000e611c91565b5061000e611b17565b5061000e611ae2565b5061000e611728565b5061000e61151a565b5061000e6114d0565b5061000e6114a8565b5061000e611456565b5061000e611437565b5061000e61140c565b5061000e6113bf565b5061000e611362565b5061000e611235565b5061000e611176565b5061000e610fe4565b5061000e610f9f565b5061000e610f4e565b5061000e610cfa565b5061000e610cab565b5061000e610c25565b5061000e610a3d565b5061000e610a0c565b5061000e610977565b5061000e61089f565b5061000e610880565b5061000e610864565b5061000e610842565b5061000e6106c4565b5061000e61069e565b5061000e610669565b5061000e6105cc565b5061000e61056d565b5061000e6104bd565b5061000e610490565b5061000e610436565b503461000e57600060031936011261000e5760206bffffffffffffffffffffffff60045460101c16604051908152f35b503461000e57600060031936011261000e57600554604080517f0000000000000000000000000000000000000000000000000de0b6b3a7640000815260609290921c69ffffffffffffffffffff16602083015290f35b0390f35b503461000e57600060031936011261000e5760206bffffffffffffffffffffffff60055416604051908152f35b503461000e57600060031936011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca168152f35b919082519283825260005b8481106105595750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161051a565b503461000e57600060031936011261000e5761048c60405161058e81611d12565b600d81527f5374616b696e6720302e312e3000000000000000000000000000000000000000602082015260405191829160208352602083019061050f565b503461000e57600060031936011261000e5760206105e8615ab9565b64e8d4a5100061063c6105f9614dad565b926bffffffffffffffffffffffff600a541693810390811161065c575b61063761062161393a565b9169ffffffffffffffffffff60095416906134e6565b6134e6565b04810390811161064f575b604051908152f35b6106576133cf565b610647565b6106646133cf565b610616565b503461000e57600060031936011261000e57602073ffffffffffffffffffffffffffffffffffffffff600f5416604051908152f35b503461000e57600060031936011261000e5760206106ba614b09565b6040519015158152f35b503461000e5760008060031936011261083f576106df614b09565b6108075761077e602061073e6107396106f733615648565b92917f204fccf0d92ed8d48f204adb39b2e81e92bad0dedb93f5716ca9478cfb57de0060806040513381528389820152846040820152866060820152a161356d565b61356d565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915291829081906044820190565b03818573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca165af180156107fa575b6107cc575b50604051f35b6107ec9060203d81116107f3575b6107e48183611d2e565b8101906139da565b50386107c6565b503d6107da565b6108026139f2565b6107c1565b604490604051907fa30a70c2000000000000000000000000000000000000000000000000000000008252600160048301526024820152fd5b80fd5b503461000e57600060031936011261000e57602060ff60085416604051908152f35b503461000e57600060031936011261000e576020610647615ab9565b503461000e57600060031936011261000e576020601154604051908152f35b503461000e57600060031936011261000e576108b96132a6565b60015460ff8160a01c1615610919577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff166001557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b503461000e57600060031936011261000e576109916132a6565b740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff6001546109db60ff8260a01c161561409e565b16176001557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b503461000e57600060031936011261000e57600c54600b546040805163ffffffff9093168352602083019190915290f35b503461000e5760008060031936011261083f57610a58614b09565b61080757610ab2610aa1610a8c3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460701c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff1690565b8015610bc957602081610ad2610acd61077e9460065461348b565b600655565b610b27610aff3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff8154169055565b7f204fccf0d92ed8d48f204adb39b2e81e92bad0dedb93f5716ca9478cfb57de0060405180610b86843383606090600092949373ffffffffffffffffffffffffffffffffffffffff608083019616825260208201528260408201520152565b0390a16040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915291829081906044820190565b6040517fe4adde72000000000000000000000000000000000000000000000000000000008152336004820152602490fd5b0390fd5b503461000e57600060031936011261000e57602060ff60015460a01c166040519015158152f35b503461000e57600060031936011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000148152f35b90815180825260208080930193019160005b828110610c81575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610c73565b503461000e57600060031936011261000e5761048c610cc8614103565b604051918291602083526020830190610c61565b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e57602060031936011261000e57600435610d1881610cdc565b610d206132a6565b803b158015610f2f575b8015610f0b575b8015610ee7575b8015610e39575b610e0f57610e0a7f5c74c441be501340b2713817a6c6975e6f3d4a4ae39fa1ac0bf75d3c54a0cad391610d957fffffffffffffffffffffffff0000000000000000000000000000000000000000600f5416600f55565b610dda8173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600d541617600d55565b610de342600e55565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390a1005b60046040517f367a1038000000000000000000000000000000000000000000000000000000008152fd5b506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527fa4c0ed3600000000000000000000000000000000000000000000000000000000600482015260208160248173ffffffffffffffffffffffffffffffffffffffff86165afa908115610eda575b600091610ebc575b5015610d3f565b610ed4915060203d81116107f3576107e48183611d2e565b38610eb5565b610ee26139f2565b610ead565b50600f5473ffffffffffffffffffffffffffffffffffffffff828116911614610d38565b50600d5473ffffffffffffffffffffffffffffffffffffffff828116911614610d31565b503073ffffffffffffffffffffffffffffffffffffffff821614610d2a565b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff600435610f8181610cdc565b166000526002602052602060ff604060002054166040519015158152f35b503461000e57600060031936011261000e576020610fbb614dad565b64e8d4a5100061063c6bffffffffffffffffffffffff600a5460601c169261063761062161393a565b503461000e57602060031936011261000e577fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d610e0a6004356110256132a6565b61102d6159c3565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810182905260208160648160007f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca73ffffffffffffffffffffffffffffffffffffffff165af18015611169575b61114b575b5061113b6110d1610aa16005546bffffffffffffffffffffffff1690565b6110d9615ab9565b906111016110f260095469ffffffffffffffffffff1690565b69ffffffffffffffffffff1690565b9061110a614b20565b91611113614dad565b937f0000000000000000000000000000000000000000000000000000000000278d009261357a565b6040519081529081906020820190565b6111629060203d81116107f3576107e48183611d2e565b50386110b3565b6111716139f2565b6110ae565b503461000e57602060031936011261000e576004356111936132a6565b61119b6159c3565b801561000e5760207f1e3be2efa25bca5bff2215c7b30b31086e703d6aa7d9b9a1f8ba62c5291219ad9161122c6111d0614dad565b6111d981613c86565b6111e1613bb5565b6bffffffffffffffffffffffff600554166111fa615ab9565b9084611204614b20565b927f0000000000000000000000000000000000000000000000000000000000278d009261357a565b604051908152a1005b503461000e5760008060031936011261083f5773ffffffffffffffffffffffffffffffffffffffff8060015416330361130457815473ffffffffffffffffffffffffffffffffffffffff16600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556112d77fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b604051913391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08484a3f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152fd5b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff60043561139581610cdc565b16600052600260205260206bffffffffffffffffffffffff60406000205460101c16604051908152f35b503461000e57602060031936011261000e577f1b930366dfeaa7eb3b325021e4ae81e36527063452ee55b86c95f85b36f4c31c60206004356113ff6132a6565b80601155604051908152a1005b503461000e57600060031936011261000e57602069ffffffffffffffffffff60095416604051908152f35b503461000e57600060031936011261000e576020600654604051908152f35b503461000e57600060031936011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419168152f35b503461000e57602060031936011261000e5760206106476004356114cb81610cdc565b614d24565b503461000e57600060031936011261000e57600554604080517f00000000000000000000000000000000000000000000003635c9adc5dea00000815260b09290921c602083015290f35b503461000e57602060031936011261000e5767ffffffffffffffff60043581811161000e573660238201121561000e57806004013591821161000e576024810190602483369201011161000e5761156f614b09565b6116f05773ffffffffffffffffffffffffffffffffffffffff91826115a9600f5473ffffffffffffffffffffffffffffffffffffffff1690565b1615610e0f576116989260209260006107396116626116236115ca33615648565b9194907f667838b33bdc898470de09e0e746990f2adc11b965b7fe6828e502ebc39e0434604051806116018d8c88878d33876141c5565b0390a1600f5473ffffffffffffffffffffffffffffffffffffffff169561356d565b93611636604051978892338b8501614204565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101875286611d2e565b604051968795869485937f4000aea000000000000000000000000000000000000000000000000000000000855260048501614231565b03927f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca165af180156116e3575b6116cc575b005b6116ca9060203d81116107f3576107e48183611d2e565b6116eb6139f2565b6116c5565b60446040517fa30a70c20000000000000000000000000000000000000000000000000000000081526001600482015260006024820152fd5b503461000e57606060031936011261000e576044356004803560243561174c6132a6565b6117546159c3565b818411611aaa576005546bffffffffffffffffffffffff811690838211611a75578269ffffffffffffffffffff8260601c1611611a4057859060b01c11611a075761179d6159d2565b936117df6117bc876106376117b660208a015160ff1690565b60ff1690565b6117d9610aa16040809901516bffffffffffffffffffffffff1690565b9061356d565b84106119d35750938291611839936000960361195e575b806118136110f260055469ffffffffffffffffffff9060601c1690565b036118cd575b508061182a6110f260055460b01c90565b0361183c575b506110d9615ab9565b51f35b6118c4816118b561186d7f816587cb2e773af4f3689a03d7520fabff3462605ded374b485b13994c0d7b5294613980565b75ffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffff000000000000000000000000000000000000000000006005549260b01b16911617600555565b85519081529081906020820190565b0390a138611830565b611955816119466118fe7fb5f554e5ef00806bace1edbb84186512ebcefa2af7706085143f501f29314df794613980565b7fffffffffffffffffffff00000000000000000000ffffffffffffffffffffffff75ffffffffffffffffffff0000000000000000000000006005549260601b16911617600555565b86519081529081906020820190565b0390a138611819565b6119a361196a846139c1565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006005541617600555565b84518381527f7f4f497e086b2eb55f8a9885ba00d33399bbe0ebcb92ea092834386435a1b9c090602090a16117f6565b84517fbc91aa3300000000000000000000000000000000000000000000000000000000815290810186815281906020010390fd5b50506040517fbc91aa33000000000000000000000000000000000000000000000000000000008152918201928352509081906020010390fd5b6040517fbc91aa3300000000000000000000000000000000000000000000000000000000815280860184815281906020010390fd5b6040517f1f3c387600000000000000000000000000000000000000000000000000000000815280860185815281906020010390fd5b50506040517fbc91aa330000000000000000000000000000000000000000000000000000000081529081019182529081906020010390fd5b503461000e57600060031936011261000e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b503461000e57604060031936011261000e57611b316132a6565b60115415611c6757611b627f0000000000000000000000000000000000000000000000000000000000000032615a1b565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048083019190915230602483015235604482015260208160648160007f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca73ffffffffffffffffffffffffffffffffffffffff165af18015611c5a575b611c3c575b6116ca611c08610aa16005546bffffffffffffffffffffffff1690565b611c10614b20565b907f0000000000000000000000000000000000000000000000000000000000278d0090602435906139ff565b611c539060203d81116107f3576107e48183611d2e565b5038611beb565b611c626139f2565b611be6565b60046040517f9f8a28f2000000000000000000000000000000000000000000000000000000008152fd5b503461000e57602060031936011261000e576020610647600435611cb481610cdc565b614c08565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117611d0557604052565b611d0d611cb9565b604052565b6040810190811067ffffffffffffffff821117611d0557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611d0557604052565b60209067ffffffffffffffff8111611d89575b60051b0190565b611d91611cb9565b611d82565b503461000e57604060031936011261000e57600435611db481610cdc565b6024359067ffffffffffffffff821161000e573660238301121561000e578160040135611de081611d6f565b92611dee6040519485611d2e565b81845260209160248386019160051b8301019136831161000e57602401905b828210611e335761048c611e218787613325565b60405190151581529081906020820190565b81358152908301908301611e0d565b90602060031983011261000e5760043567ffffffffffffffff9283821161000e578060238301121561000e57816004013593841161000e5760248460051b8301011161000e576024019190565b503461000e57611e9e36611e42565b90611ea76132a6565b63ffffffff600c54161515806121ec575b6121b457611ed4611ece6110f260055460b01c90565b836134e6565b611edc615ae7565b80821161217957505060005b828110611f49576116ca611f1584611f0f611f0960045460ff9060081c1690565b91614090565b90615401565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff006004549260081b16911617600455565b611f8f611f88611f62611f5d848787613fdf565b613ff7565b73ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460ff1690565b61211c57611fbf610aa1611faa611f62611f5d858888613fdf565b5460101c6bffffffffffffffffffffffff1690565b6120be57611fda610aa1610a8c611f62611f5d858888613fdf565b6120605780612021611ff6611f62611f5d61205b958888613fdf565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b7fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d612053610de3611f5d848888613fdf565b0390a16133ff565b611ee8565b611f5d90610bfa9361207193613fdf565b6040517f7a378b9c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b611f5d90610bfa936120cf93613fdf565b6040517f602d4d1100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b611f5d90610bfa9361212d93613fdf565b6040517ea5216600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b6040517f35cf446b00000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b60446040517fa30a70c20000000000000000000000000000000000000000000000000000000081526000600482015260016024820152fd5b506121f5614b09565b15611eb8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff8111612237575b01160190565b61223f611cb9565b612231565b503461000e57606060031936011261000e5760043561226281610cdc565b6044359067ffffffffffffffff821161000e573660238301121561000e5781600401359061228f826121fb565b9161229d6040519384611d2e565b808352366024828601011161000e5760208160009260246116ca9701838701378401015260243590614ded565b503461000e57600060031936011261000e576020610647614dad565b503461000e576122f536611e42565b6122fd6132a6565b60005b6003548110156123725780612368612340611f6261232061236d95615b42565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff8154169055565b6133ff565b612300565b5061237b615b87565b60005b8181106123c15750816123b2827f40aed8e423b39a56b445ae160f4c071fc2cfb48ee0b6dcd5ffeb6bc5b18d10d094615bda565b610e0a60405192839283615cb7565b6123cf611f5d828486613fdf565b6124036123ff611f888373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b1590565b6124ec5761243e6124348273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460081c60ff1690565b6124a4579061236861247361249f9373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b6101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff825416179055565b61237e565b6040517ea5216600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b6040517feac13dcd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e57602060031936011261000e5760206106ba60043561255881610cdc565b61496a565b503461000e5761256c36611e42565b906125756132a6565b61257d6159c3565b61258d612588614dad565b613c86565b60005b8281106125b4576116ca611f156125a685614090565b60045460081c60ff1661407c565b6125c2611f5d828585613fdf565b6125f46125ef8273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b614001565b906126026123ff8351151590565b6124ec5760208201516129f6576126df92917f2360404a74478febece1a14f11275f22ada88d19ef96f7d785913010bfff447991612053612656610aa16040809501516bffffffffffffffffffffffff1690565b92836126e4575b6126b261268a8473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055565b51928392836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b612590565b6127f66127ac6126fb6126f686614c08565b6139c1565b61275861271f600a9261271a84546bffffffffffffffffffffffff1690565b613dcf565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a541617600a55565b61278761271f6127726126f661276c61393a565b8b6138d2565b83546bffffffffffffffffffffffff16613dcf565b61271a6127966126f688614d24565b915460601c6bffffffffffffffffffffffff1690565b7fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b61283b61280d61280860085460ff1690565b61404b565b60ff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006008541617600855565b6128906128688473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff8154169055565b61299c610acd6bffffffffffffffffffffffff6128ac876139c1565b61291a6128ce8261271a6004546bffffffffffffffffffffffff9060701c1690565b7fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff79ffffffffffffffffffffffff00000000000000000000000000006004549260701b16911617600455565b612993816129488973ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b907fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff79ffffffffffffffffffffffff000000000000000000000000000083549260701b169116179055565b1660065461356d565b6129f16129c98473ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008154169055565b61265d565b6040517fded6031900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e5760008060031936011261083f57612a5a6159c3565b612a8a610aa1611faa3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b8015612e0a5760409081517ffeaf968c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9160a082600481867f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419165afa918215612dfd575b85908693612dbd575b5069ffffffffffffffffffff16916010548314612d8c57612b4b7f0000000000000000000000000000000000000000000000000000000000002a308261356d565b4210612d2d57612b7c907f0000000000000000000000000000000000000000000000000000000000002ee09061356d565b4210908180612d56575b612d2d5792827fd2720e8f454493f612cc97499fe8cbce7fa4d4c18d346fe7104e9042df1c1edd612bc6612c21948997612bc1602098601055565b61596b565b875133815260208101939093526040830181905291606090a185517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915293849283919082906044820190565b03927f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca165af18015612d20575b612d02575b50612cae612c5f614dad565b612c67614103565b7f000000000000000000000000000000000000000000000000000000000076a7007f00000000000000000000000000000000000000000000003635c9adc5dea00000614366565b611839612ccb610aa16005546bffffffffffffffffffffffff1690565b612cd3615ab9565b612ceb6110f260095469ffffffffffffffffffff1690565b612cf3614b20565b91612cfc614dad565b936136c4565b612d199060203d81116107f3576107e48183611d2e565b5038612c53565b612d286139f2565b612c4e565b600485517ffc53c50a000000000000000000000000000000000000000000000000000000008152fd5b50612d876123ff611f883373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b612b86565b84517ff3553c2200000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b69ffffffffffffffffffff9350612deb915060a03d8111612df6575b612de38183611d2e565b81019061427a565b50949150612b0a9050565b503d612dd9565b612e056139f2565b612b01565b60046040517fef67f5d8000000000000000000000000000000000000000000000000000000008152fd5b503461000e57600060031936011261000e576020610647614b20565b503461000e57600060031936011261000e57612e6a6132a6565b612e726159c3565b612e7a615ab9565b612f437fffffffffffffffff00000000000000000000000000000000000000000000000077ffffffffffffffffffffffff000000000000000000000000612f73612f27612f21612f1b612ecb614dad565b97612ed589613c86565b612edd613bb5565b888103908111612fd5575b610637612ef361393a565b9169ffffffffffffffffffff60095416948591612f1b8561063764e8d4a51000998a946134e6565b046139c1565b9a6134e6565b600a54956bffffffffffffffffffffffff958691828916613dcf565b1694857fffffffffffffffffffffffffffffffffffffffff00000000000000000000000088161760601c16613dcf565b60601b1692161717600a5542600b557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006004541660045560006040517ff7d0e0f15586495da8c687328ead30fb829d9da55538cb0ef73dd229e517cdb88282a1f35b612fdd6133cf565b612ee8565b503461000e57600060031936011261000e57612ffc6132a6565b600d5473ffffffffffffffffffffffffffffffffffffffff168015610e0f57613026600e5461355d565b4210612e0a576130719073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600f541617600f55565b61309e7fffffffffffffffffffffffff0000000000000000000000000000000000000000600d5416600d55565b7ffa33c052bbee754f3c0482a89962daffe749191fa33c696a61e947fbfd68bd84610e0a610de3600f5473ffffffffffffffffffffffffffffffffffffffff1690565b503461000e5760008060031936011261083f576130fc6132a6565b613104614b09565b6108075761077e6020613141613118614b20565b600a54906131366bffffffffffffffffffffffff918284169061348b565b9160601c169061348b565b6040518181527f150a6ec0e6f4e9ddcaaaa1674f157d91165a42d60653016f87a9fc870a39f050908060208101610b86565b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff6004356131a681610cdc565b6131ae6132a6565b1633811461324857806000917fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015561321d613204835473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b90604051917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788484a3f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff6000541633036132c757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152fd5b90601154801561336c576133699260405173ffffffffffffffffffffffffffffffffffffffff60208201921682526020815261336081611d12565b51902091613374565b90565b505050600190565b929091906000915b84518310156133c75761338f8386613469565b51908181116133b2576000526020526133ac6040600020926133ff565b9161337c565b906000526020526133ac6040600020926133ff565b915092501490565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461342d570190565b6134356133cf565b0190565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602091815181101561347e575b60051b010190565b613486613439565b613476565b9190820391821161349857565b6134a06133cf565b565b64e8d4a5100090807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166134da570290565b6134e26133cf565b0290565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166134da570290565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115613551570490565b613559613517565b0490565b9062093a80820180921161349857565b9190820180921161349857565b929394916135df906135d961358f85856138f5565b956135d36135b061359f88613957565b8b8a829d039081116136b75761348b565b6135c86110f260095469ffffffffffffffffffff1690565b850361366f576134a2565b926134e6565b90613547565b938410613645576136409464e8d4a51000613633866106378661361e61271f6126f661363a9b6117d9876136196127ac9e6126f69e61348b565b6138d2565b69ffffffffffffffffffff60095416906134e6565b049061356d565b4261356d565b600b55565b60046040517fda056d00000000000000000000000000000000000000000000000000000000008152fd5b6136b261367b86613980565b69ffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000006009541617600955565b6134a2565b6136bf6133cf565b61348b565b928491926136d283856138f5565b906137156136df85613957565b96838503948511613826575b878503948511613819575b6135d369ffffffffffffffffffff958660095416850361366f576134a2565b90811561380c575b0461372a8197829661348b565b90613734916138d2565b61373d9161356d565b613746906139c1565b613783906bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a541617600a55565b60095416613790916134e6565b9061379a916134e6565b64e8d4a5100090046137ab9161356d565b6137b4906139c1565b613802907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b613640904261356d565b613814613517565b61371d565b6138216133cf565b6136f6565b61382e6133cf565b6136eb565b6136bf93916135d9613899926135d3600a5469ffffffffffffffffffff61388c6bffffffffffffffffffffffff988984169b8c9a61386f61393a565b506009549560601c169a8b92600b544281116138b6575b5061348b565b9116850361366f576134a2565b918210613645576127ac61363a916126f661271f613640966139c1565b428103116138c5575b38613886565b6138cd6133cf565b6138bf565b64e8d4a51000916106376135599269ffffffffffffffffffff60095416906134e6565b61392564e8d4a51000916bffffffffffffffffffffffff600a541693810390811161065c5761063761062161393a565b0481039081116139325790565b6133696133cf565b600b5442811161394a5750600090565b4281039081116139325790565b64e8d4a510006139256bffffffffffffffffffffffff600a5460601c169261063761062161393a565b69ffffffffffffffffffff90818111613997571690565b60046040517f811752de000000000000000000000000000000000000000000000000000000008152fd5b6bffffffffffffffffffffffff90818111613997571690565b9081602091031261000e5751801515810361000e5790565b506040513d6000823e3d90fd5b600c5463ffffffff949385821661000e5783613b3f93613b3a7f125fc8494f786b470e3c39d0932a62e9e09e291ebd81ea19c57604f6d2b1d16798889569ffffffffffffffffffff613a5086613980565b167fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000060095416176009557fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000613aa442613b84565b928316911617600c556008547fffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffff70ffffffff000000000000000000000000008360681b169116176008557fffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffff79ffffffff000000000000000000000000000000000000000000006009549260b01b16911617600955565b613833565b613b7f613b51600c5463ffffffff1690565b600b5490604051948594859094939263ffffffff906060936080840197845260208401521660408201520152565b0390a1565b63ffffffff90818111613997571690565b9190916bffffffffffffffffffffffff8080941691160191821161349857565b613bc2600b544290613fcd565b600954907fffffffffffff00000000000000000000000000000000ffffffffffffffffffff79ffffffff00000000000000000000000000000000000000000000613c6c75ffffffffffffffffffffffff00000000000000000000613c62613c4b6126f663ffffffff8960b01c168803888111613c79575b69ffffffffffffffffffff8a166134e6565b6bffffffffffffffffffffffff8860501c16613b95565b60501b1693613b84565b60b01b1692161717600955565b613c816133cf565b613c39565b6126f6613c9291613cfd565b6008547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff6cffffffffffffffffffffffff0070ffffffff00000000000000000000000000613ceb613ce6600b544290613fcd565b613b84565b60681b169360081b1691161717600855565b61336990613d0d600b5442101590565b15613d82576bffffffffffffffffffffffff613d7864e8d4a51000613d6b613d4f600b54613d4963ffffffff60085460681c1663ffffffff1690565b9061348b565b6106376008549669ffffffffffffffffffff60095416906134e6565b046135d960ff8516613dbe565b9160081c1661356d565b6bffffffffffffffffffffffff613d7864e8d4a51000613d6b63ffffffff60085460681c16420342811115613d4f57613db96133cf565b613d4f565b60018110613dc95790565b50600190565b6bffffffffffffffffffffffff918216908216039190821161349857565b6001906bffffffffffffffffffffffff80911690811461342d570190565b6127ac906134a092613e1b61393a565b69ffffffffffffffffffff6009541691610637613e5984612f1b85610637613e528a612f1b64e8d4a510009a8b998a9889946134e6565b98876134e6565b9406613ecd575b06613eb8575b613ea7907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a54916bffffffffffffffffffffffff938491828516613b95565b1691161780600a5560601c16613b95565b90613ec5613ea791613ded565b919050613e66565b91613ed790613ded565b91613e60565b613f556134a09164e8d4a51000613f1181612f1b613ef961393a565b61063769ffffffffffffffffffff60095416876134e6565b9106613fbf575b600a547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006bffffffffffffffffffffffff80948194828516613b95565b1691161780600a5560601c16908111613fb2577fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b613fba6133cf565b6127ac565b613fc890613ded565b613f18565b9080821015613fda575090565b905090565b9190811015613fef5760051b0190565b611d91613439565b3561336981610cdc565b9060405161400e81611ce9565b606081935460ff81161515835260ff8160081c16151560208401526bffffffffffffffffffffffff90818160101c16604085015260701c16910152565b60ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9116019060ff821161349857565b9060ff8091169116039060ff821161349857565b60ff81116139975760ff1690565b156140a557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b60405190600354808352826020918282019060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b936000905b828210614156575050506134a092500383611d2e565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958895509381019390910190614140565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b919260a09373ffffffffffffffffffffffffffffffffffffffff6133699896931684526020840152604083015260608201528160808201520191614186565b60409073ffffffffffffffffffffffffffffffffffffffff61336995931681528160208201520191614186565b613369939273ffffffffffffffffffffffffffffffffffffffff6060931682526020820152816040820152019061050f565b519069ffffffffffffffffffff8216820361000e57565b908160a091031261000e5761428e81614263565b91602082015191604081015191613369608060608401519301614263565b906142b682611d6f565b6142c36040519182611d2e565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06142f18294611d6f565b0190602036910137565b90815180825260208080930193019160005b82811061431b575050505090565b83518552938101939281019260010161430d565b916143589061434a6133699593606086526060860190610c61565b9084820360208601526142fb565b9160408184039101526142fb565b939291909360ff61437960085460ff1690565b1615614522578264e8d4a510006143a6876106376143ad9569ffffffffffffffffffff60095416906134e6565b0495614529565b91600091826143bc82516142ac565b946143c783516142ac565b92855b81518710156144a1576143fa6143e08884613469565b5173ffffffffffffffffffffffffffffffffffffffff1690565b8861442b610aa1611faa8473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b8c811561449257898461447a946144578e61445161448b9a976144859a61445e986145b5565b92613469565b5288614723565b6144688b8a613469565b526144738a8c613469565b519061356d565b946144738988613469565b966133ff565b95926143ca565b50505050929561448b906133ff565b7e635ea9da6e262e92bb713d71840af7c567807ff35bf73e927490c6128324809899506144fd919650613b7f95506127ac9250926126f661271f6144e7614516966139c1565b600a546bffffffffffffffffffffffff16613dcf565b600a5460601c6bffffffffffffffffffffffff16613dcf565b6040519384938461432f565b5050509050565b64e8d4a5100061459960ff92610637604051956060870187811067ffffffffffffffff8211176145a8575b60405263ffffffff60085487811689526bffffffffffffffffffffffff8160081c1660208a015260681c16604088015269ffffffffffffffffffff60095416906134e6565b04915116908115613551570490565b6145b0611cb9565b614554565b916146b061467361336993946145cd600b5442101590565b156146f45761463761462c6145f0600b5463ffffffff60095460b01c169061348b565b955b61462661460e6009549869ffffffffffffffffffff8a166134e6565b6bffffffffffffffffffffffff809960501c1661356d565b906134e6565b64e8d4a51000900490565b90846146638873ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b541682039182116146e757613fcd565b936146a7614680866139c1565b9173ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b92835416613b95565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000825416179055565b6146ef6133cf565b613fcd565b61463761462c63ffffffff60095460b01c164203428111614716575b956145f2565b61471e6133cf565b614710565b916148066147ed613369939461473b600b5442101590565b1561484e576147ae61479664e8d4a5100061478961476d600b54613d4963ffffffff60085460681c1663ffffffff1690565b6106376008549969ffffffffffffffffffff60095416906134e6565b046135d960ff8816613dbe565b6bffffffffffffffffffffffff809660081c1661356d565b90846147da8873ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5460601c1682039182116146e757613fcd565b936147fa614680866139c1565b92835460601c16613b95565b7fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b6147ae61479664e8d4a5100061478963ffffffff60085460681c1642034281111561476d5761487b6133cf565b61476d565b600b546133699161462c914281116148f45763ffffffff60095460b01c1681039081116148e7575b905b6148d56009549269ffffffffffffffffffff6bffffffffffffffffffffffff8560501c1694166134e6565b8201809211156134e6576106376133cf565b6148ef6133cf565b6148a8565b5063ffffffff60095460b01c164203428111614911575b906148aa565b6149196133cf565b61490b565b9061493d73ffffffffffffffffffffffffffffffffffffffff91613cfd565b911660005260076020526bffffffffffffffffffffffff60406000205460601c1681039081116139325790565b61499a610aa1611faa8373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b15614b03576149aa6123ff614b09565b614b03576040517ffeaf968c00000000000000000000000000000000000000000000000000000000815260a08160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419165afa908115614af6575b6000908192614ad1575b5069ffffffffffffffffffff601054911614614aca57614a657f0000000000000000000000000000000000000000000000000000000000002a308261356d565b4210614aca57614a96907f0000000000000000000000000000000000000000000000000000000000002ee09061356d565b421015613dc957611f886133699173ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5050600090565b9050614aeb915060a03d8111612df657612de38183611d2e565b509291505038614a25565b614afe6139f2565b614a1b565b50600090565b60ff6004541680614b175790565b50600b54421090565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca165afa908115614bfb575b600091614bca575b50614ba4615ab9565b8103908111614bbd575b60065481039081116139325790565b614bc56133cf565b614bae565b906020823d8211614bf3575b81614be360209383611d2e565b8101031261083f57505138614b9b565b3d9150614bd6565b614c036139f2565b614b93565b73ffffffffffffffffffffffffffffffffffffffff81169060009180835260026020526bffffffffffffffffffffffff80604085205460101c16938415614cf95760ff604082205416614cd357505050613d49610aa1614cc1614c9a614c9561336996613d497f000000000000000000000000000000000000000000000000000000000000001482613547565b614880565b9373ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b546bffffffffffffffffffffffff1690565b909250614ce260409294614880565b938152600760205220541681039081116139325790565b935050505090565b908015614d17575b810481039081116139325790565b614d1f613517565b614d09565b73ffffffffffffffffffffffffffffffffffffffff81166000526002602052604060002060405190614d5582611ce9565b549060ff821615908115815260ff8360081c161515602082015260606bffffffffffffffffffffffff808560101c169485604085015260701c16910152614aca5715614b035761336990614da7614dad565b9061491e565b6bffffffffffffffffffffffff60045460101c167f0000000000000000000000000000000000000000000000000000000000000014908115613551570490565b9173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca169182330361502657614e3f60ff60015460a01c161561409e565b614e476159c3565b809264e8d4a51000808310614ff05782069182614f3f575b505050614e8f611f888473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b15614e9e57506134a091615415565b6011549081614eb3575b50506134a0916150ca565b805115612e0a57614f3491614ed4826020806123ff95518301019101615050565b90604051602081019061336081614f088a8591909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611d2e565b612e0a573880614ea8565b829450602091614f54600094614fae9361348b565b956040519485809481937fa9059cbb0000000000000000000000000000000000000000000000000000000083528b600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015614fe3575b614fc5575b8080614e5f565b614fdc9060203d81116107f3576107e48183611d2e565b5038614fbe565b614feb6139f2565b614fb9565b6040517f1d820b1700000000000000000000000000000000000000000000000000000000815264e8d4a510006004820152602490fd5b60046040517f4d695438000000000000000000000000000000000000000000000000000000008152fd5b602090818184031261000e5780519067ffffffffffffffff821161000e57019180601f8401121561000e57825161508681611d6f565b936150946040519586611d2e565b818552838086019260051b82010192831161000e578301905b8282106150bb575050505090565b815181529083019083016150ad565b91906150fc610aa1611faa8573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b92615107828561356d565b937f0000000000000000000000000000000000000000000000000de0b6b3a76400008086106153bc575060055461514c9060601c69ffffffffffffffffffff166110f2565b80861161537857505061515d615ae7565b8083116153455750613b7f7f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909394615196612588614dad565b6152357f000000000000000000000000000000000000000000000000000000000000001461522f6151d06151ca8389613547565b8861348b565b916152296151e06126f685614880565b6146b061520d8a73ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b9161522483546bffffffffffffffffffffffff1690565b613b95565b87613547565b90613e0b565b61529d61525d615244866139c1565b60045460101c6bffffffffffffffffffffffff16613b95565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff6dffffffffffffffffffffffff00006004549260101b16911617600455565b6153126152a9826139c1565b6152d38573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b907fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff6dffffffffffffffffffffffff000083549260101b169116179055565b6040519384938460409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b6040517fb94339d80000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b610bfa916153859161348b565b6040519182917fb94339d8000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b6040517f1d820b170000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b60ff60019116019060ff821161349857565b9060ff8091169116019060ff821161349857565b9190615447610aa1611faa8573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b92615452828561356d565b937f00000000000000000000000000000000000000000000003635c9adc5dea000008086106153bc575061548b6110f260055460b01c90565b80861161537857507f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909394613b7f911561552d575b6154fc6154cf6126f686614880565b6146b061520d8673ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b6155246128ce61550b866139c1565b60045460701c6bffffffffffffffffffffffff16613b95565b61529d84613edd565b615538612588614dad565b61555361280d60085460ff81169081156155e2575b506153ef565b600880546155dd911c6bffffffffffffffffffffffff166155948573ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b6154c0565b6127ac6bffffffffffffffffffffffff6156159260081c1661271a600a546bffffffffffffffffffffffff9060601c1690565b6156427fffffffffffffffffffffffffffffffffffffff000000000000000000000000ff60085416600855565b3861554d565b6156756125ef8273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b9160408301926bffffffffffffffffffffffff90816156a086516bffffffffffffffffffffffff1690565b16156158cc576156c1906156b660045460ff1690565b615887575b51151590565b156157ed57506156fc6128ce6156e385516bffffffffffffffffffffffff1690565b60045460701c6bffffffffffffffffffffffff16613dcf565b6157e8610aa16157248461571f610aa188516bffffffffffffffffffffffff1690565b615913565b946157b861286861579161574c610aa16008546bffffffffffffffffffffffff9060081c1690565b613d49610aa161577c8b73ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5460601c6bffffffffffffffffffffffff1690565b9673ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b6157c761271f6144e7886139c1565b6157d66127ac6144fd876139c1565b516bffffffffffffffffffffffff1690565b929190565b836158716128686157918661571f610aa19661583961525d6158206158809b9c9d516bffffffffffffffffffffffff1690565b60045460101c6bffffffffffffffffffffffff16613dcf565b86516bffffffffffffffffffffffff167f00000000000000000000000000000000000000000000000000000000000000149116614d01565b6157d661271f6144e7876139c1565b9190600090565b615892612588614dad565b61589a613bb5565b6158c77fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060045416600455565b6156bb565b6040517fe4adde7200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602490fd5b73ffffffffffffffffffffffffffffffffffffffff64e8d4a5100061594d6bffffffffffffffffffffffff938460095460501c16906134e6565b04921660005260076020526040600020541681039081116139325790565b9061599e576133699060057f00000000000000000000000000000000000000000000017b7883c069166000009104613fcd565b507f00000000000000000000000000000000000000000000017b7883c0691660000090565b6159cb614b09565b156121b457565b604051906159df82611ce9565b81606060045460ff81161515835260ff8160081c1660208401526bffffffffffffffffffffffff90818160101c16604085015260701c16910152565b6004549060ff8260081c1690808210615a825750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060019116176004557fded6ebf04e261e1eb2f3e3b268a2e6aee5b478c15b341eba5cf18b9bc80c2e636000604051a1565b60449250604051917fe709379900000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b615ac16159d2565b6bffffffffffffffffffffffff6060816040840151169201511681018091116139325790565b615aef6159d2565b6005549060406bffffffffffffffffffffffff91615b1c8385169460ff6020840151169060b01c906134e6565b8403938411615b35575b01511681039081116139325790565b615b3d6133cf565b615b26565b600354811015615b7a575b60036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b615b82613439565b615b4d565b60035460008060035581615b99575050565b600381527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b918201915b828110615bcf57505050565b818155600101615bc3565b67ffffffffffffffff8211615caa575b680100000000000000008211615c9d575b60035482600355808310615c5c575b50600360005260005b828110615c1f57505050565b60019060208335615c2f81610cdc565b930192817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b015501615c13565b827fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b91820191015b818110615c915750615c0a565b60008155600101615c84565b615ca5611cb9565b615bfb565b615cb2611cb9565b615bea565b90916040602092828482018583525201929160005b828110615cda575050505090565b90919293828060019273ffffffffffffffffffffffffffffffffffffffff8835615d0381610cdc565b16815201950193929101615ccc56fea164736f6c6343000810000a

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

000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841900000000000000000000000000000000000000000014adf4b7320334b900000000000000000000000000000000000000000000000000017b7883c06916600000000000000000000000000000000000000000000000000a968163f0a57b4000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002ee000000000000000000000000000000000000000000000017b7883c0691660000000000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000278d00000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000000014

-----Decoded View---------------
Arg [0] : params (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
14 Constructor Arguments found :
Arg [0] : 000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca
Arg [1] : 0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419
Arg [2] : 00000000000000000000000000000000000000000014adf4b7320334b9000000
Arg [3] : 00000000000000000000000000000000000000000000017b7883c06916600000
Arg [4] : 000000000000000000000000000000000000000000000a968163f0a57b400000
Arg [5] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [6] : 00000000000000000000000000000000000000000000003635c9adc5dea00000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000002a30
Arg [8] : 0000000000000000000000000000000000000000000000000000000000002ee0
Arg [9] : 00000000000000000000000000000000000000000000017b7883c06916600000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000032
Arg [11] : 0000000000000000000000000000000000000000000000000000000000278d00
Arg [12] : 000000000000000000000000000000000000000000000000000000000076a700
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000014


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  ]
[ 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.