ETH Price: $2,982.44 (+2.31%)
Gas: 13 Gwei

Contract

0x996913c8c08472f584ab8834e925b06D0eb1D813
 
Transaction Hash
Method
Block
From
To
Value
0xe30b35dc8a46800e8ab06d7e1087473a4289abc309dc757ae13d578234c9b265Claim Reward(pending)2024-05-02 12:52:306 mins ago1714654350IN
0x996913c8...D0eb1D813
0 ETH(Pending)(Pending)
0x003661ea8830d99014557568af6424faaecc1cafbb1ad08f4082f539e9fae537Claim Reward(pending)2024-05-02 12:45:1514 mins ago1714653915IN
0x996913c8...D0eb1D813
0 ETH(Pending)(Pending)
Claim Reward197825582024-05-02 12:52:356 mins ago1714654355IN
0x996913c8...D0eb1D813
0 ETH0.0014850315.6864414
Claim Reward197825222024-05-02 12:45:2314 mins ago1714653923IN
0x996913c8...D0eb1D813
0 ETH0.0017743113.76828277
Claim Reward197819252024-05-02 10:45:112 hrs ago1714646711IN
0x996913c8...D0eb1D813
0 ETH0.000718516.42848185
Claim Reward197813412024-05-02 8:47:354 hrs ago1714639655IN
0x996913c8...D0eb1D813
0 ETH0.000692636.19692508
Claim Reward197813372024-05-02 8:46:474 hrs ago1714639607IN
0x996913c8...D0eb1D813
0 ETH0.000635086.26603878
Claim Reward197813352024-05-02 8:46:234 hrs ago1714639583IN
0x996913c8...D0eb1D813
0 ETH0.000616356.51055828
Claim Reward197813072024-05-02 8:40:474 hrs ago1714639247IN
0x996913c8...D0eb1D813
0 ETH0.000606696.40855238
Claim Reward197811112024-05-02 8:01:354 hrs ago1714636895IN
0x996913c8...D0eb1D813
0 ETH0.000713176.38074844
Claim Reward197810972024-05-02 7:58:475 hrs ago1714636727IN
0x996913c8...D0eb1D813
0 ETH0.000575775.15142475
Claim Reward197810872024-05-02 7:56:475 hrs ago1714636607IN
0x996913c8...D0eb1D813
0 ETH0.0005995.35923358
Claim Reward197810772024-05-02 7:54:475 hrs ago1714636487IN
0x996913c8...D0eb1D813
0 ETH0.000677986.06588285
Claim Reward197809152024-05-02 7:22:235 hrs ago1714634543IN
0x996913c8...D0eb1D813
0 ETH0.000671637.09443874
Claim Reward197800942024-05-02 4:36:238 hrs ago1714624583IN
0x996913c8...D0eb1D813
0 ETH0.000583845.22362719
Claim Reward197800792024-05-02 4:33:238 hrs ago1714624403IN
0x996913c8...D0eb1D813
0 ETH0.000583185.21772371
Claim Reward197797942024-05-02 3:35:599 hrs ago1714620959IN
0x996913c8...D0eb1D813
0 ETH0.000671975.21433986
Claim Reward197797822024-05-02 3:33:359 hrs ago1714620815IN
0x996913c8...D0eb1D813
0 ETH0.000744415.77645538
Claim Reward197791862024-05-02 1:33:2311 hrs ago1714613603IN
0x996913c8...D0eb1D813
0 ETH0.000462534.88577175
Claim Reward197788412024-05-02 0:23:2312 hrs ago1714609403IN
0x996913c8...D0eb1D813
0 ETH0.000396124.27454294
Claim Reward197784602024-05-01 23:06:4713 hrs ago1714604807IN
0x996913c8...D0eb1D813
0 ETH0.000450274.75624562
Claim Reward197784492024-05-01 23:04:3513 hrs ago1714604675IN
0x996913c8...D0eb1D813
0 ETH0.000635426.71196801
Claim Reward197783132024-05-01 22:37:1114 hrs ago1714603031IN
0x996913c8...D0eb1D813
0 ETH0.000485695.2410888
Claim Reward197783002024-05-01 22:34:3514 hrs ago1714602875IN
0x996913c8...D0eb1D813
0 ETH0.000570515.10432581
Claim Reward197782502024-05-01 22:24:3514 hrs ago1714602275IN
0x996913c8...D0eb1D813
0 ETH0.000530294.83095203
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RewardVault

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 34 : RewardVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
import {TypeAndVersionInterface} from
  "@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol";

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

import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";

import {IRewardVault} from "../interfaces/IRewardVault.sol";
import {IStakingPool} from "../interfaces/IStakingPool.sol";
import {PausableWithAccessControl} from "../PausableWithAccessControl.sol";
import {CommunityStakingPool} from "../pools/CommunityStakingPool.sol";
import {OperatorStakingPool} from "../pools/OperatorStakingPool.sol";

/// @notice This contract is the reward vault for the staking pools. Admin can deposit rewards into
/// the vault and set the aggregate reward rate for each pool to control the reward distribution.
/// @dev This contract interacts with the community and operator staking pools that it is connected
/// to. A reward vault is connected to only one community and operator staking pool during its
/// lifetime, which means when we upgrade either one of the pools or introduce a new type of pool,
/// we will need to update this contract and deploy a new reward vault.
/// @dev invariant LINK balance of the contract is greater than or equal to the sum of unvested
/// rewards.
/// @dev invariant The sum of all stakers' rewards is less than or equal to the sum of available
/// rewards.
/// @dev invariant The reward bucket with zero aggregate reward rate has zero reward.
/// @dev invariant Stakers' multipliers are within 0 and the max value.
/// @dev We only support LINK token in v0.2 staking. Rebasing tokens, ERC777 tokens, fee-on-transfer
/// tokens or tokens that do not have 18 decimal places are not supported.
contract RewardVault is IRewardVault, PausableWithAccessControl, TypeAndVersionInterface {
  using FixedPointMathLib for uint256;
  using SafeCast for uint256;

  /// @notice This error is thrown when the pool address is not one of the registered staking pools
  error InvalidPool();

  /// @notice This error is thrown when the reward amount is invalid when adding rewards
  error InvalidRewardAmount();

  /// @notice This error is thrown when the aggregate reward rate is invalid when adding rewards
  error InvalidEmissionRate();

  /// @notice This error is thrown when the delegation rate is invalid when setting delegation rate
  error InvalidDelegationRate();

  /// @notice This error is thrown when an address who doesn't have access tries to call a function
  /// For example, when the caller is not a rewarder and adds rewards to the vault, or
  /// when the caller is not a staking pool and tries to call updateRewardPerToken.
  error AccessForbidden();

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

  /// @notice This error is thrown when the reward duration is too short when adding rewards
  error RewardDurationTooShort();

  /// @notice this error is thrown when the rewards remaining are insufficient for the new
  /// delegation rate
  error InsufficentRewardsForDelegationRate();

  /// @notice This error is thrown when calling an operation that is not allowed when the vault is
  /// closed.
  error VaultAlreadyClosed();

  /// @notice This error is thrown when the staker tries to claim rewards and the staker has no
  /// rewards to claim.
  error NoRewardToClaim();

  /// @notice This event is emitted when the delegation rate is updated.
  /// @param oldDelegationRate The old delegationRate
  /// @param newDelegationRate The new delegationRate
  event DelegationRateSet(uint256 oldDelegationRate, uint256 newDelegationRate);

  /// @notice This event is emitted when rewards are added to the vault
  /// @param pool The pool to which the rewards are added
  /// @param amount The reward amount
  /// @param emissionRate The target aggregate reward rate (token/second)
  event RewardAdded(address indexed pool, uint256 amount, uint256 emissionRate);

  /// @notice This event is emitted when the vault is opened.
  event VaultOpened();

  /// @notice This event is emitted when the vault is closed.
  /// @param totalUnvestedRewards The total amount of unvested rewards at the
  /// time the vault was closed
  event VaultClosed(uint256 totalUnvestedRewards);

  /// @notice This event is emitted when the staker claims rewards
  event RewardClaimed(address indexed staker, uint256 claimedRewards);

  /// @notice This event is emitted when the forfeited rewards are shared back into the reward
  /// buckets.
  /// @param vestedReward The amount of forfeited rewards shared in juels
  /// @param vestedRewardPerToken The amount of forfeited rewards per token added.
  /// @param reclaimedReward The amount of forfeited rewards reclaimed.
  /// @param isOperatorReward True if the forfeited reward is from the operator staking pool.
  event ForfeitedRewardDistributed(
    uint256 vestedReward,
    uint256 vestedRewardPerToken,
    uint256 reclaimedReward,
    bool isOperatorReward
  );

  /// @notice This event is emitted when the community pool rewards are updated
  /// @param baseRewardPerToken The per-token base reward of the community staking pool
  /// pool
  event CommunityPoolRewardUpdated(uint256 baseRewardPerToken);

  /// @notice This event is emitted when the operator pool rewards are updated
  /// @param baseRewardPerToken The per-token base reward of the operator staking pool
  /// @param delegatedRewardPerToken The per-token delegated reward of the operator staking
  /// pool
  event OperatorPoolRewardUpdated(uint256 baseRewardPerToken, uint256 delegatedRewardPerToken);

  /// @notice This event is emitted when a staker's rewards are updated
  /// @param staker The staker address
  /// @param vestedBaseReward The staker's vested base rewards
  /// @param vestedDelegatedReward The staker's vested delegated rewards
  /// @param baseRewardPerToken The staker's base reward per token
  /// @param operatorDelegatedRewardPerToken The staker's delegated reward per token
  /// @param claimedBaseRewardsInPeriod The staker's claimed base rewards in the period
  event StakerRewardUpdated(
    address indexed staker,
    uint256 vestedBaseReward,
    uint256 vestedDelegatedReward,
    uint256 baseRewardPerToken,
    uint256 operatorDelegatedRewardPerToken,
    uint256 claimedBaseRewardsInPeriod
  );

  /// @notice This event is emitted when the staker rewards are finalized
  /// @param staker The staker address
  /// @param shouldForfeit True if the staker forfeited their rewards
  event RewardFinalized(address indexed staker, bool shouldForfeit);

  /// @notice The constructor parameters.
  struct ConstructorParams {
    /// @notice The LINK token.
    LinkTokenInterface linkToken;
    /// @notice The community staking pool.
    CommunityStakingPool communityStakingPool;
    /// @notice The operator staking pool.
    OperatorStakingPool operatorStakingPool;
    /// @notice The delegation rate expressed in basis points. For example, a delegation rate of
    /// 4.5% would be represented as 450 basis points.
    uint32 delegationRate;
    /// @notice The time it takes for a multiplier to reach its max value in seconds.
    uint32 multiplierDuration;
    /// @notice The time it requires to transfer admin role
    uint48 adminRoleTransferDelay;
  }

  /// @notice This struct is used to store the reward information for a reward bucket.
  struct RewardBucket {
    /// @notice The reward aggregate reward rate of the reward bucket in Juels/second.
    uint80 emissionRate;
    /// @notice The timestamp when the reward duration ends.
    uint80 rewardDurationEndsAt;
    /// @notice The last updated available reward per token of the reward bucket.
    /// This value only increases over time as more rewards vest to the
    /// stakers.
    uint80 vestedRewardPerToken;
  }

  /// @notice This struct is used to store the reward buckets states.
  struct RewardBuckets {
    /// @notice The reward bucket for the operator staking pool.
    RewardBucket operatorBase;
    /// @notice The reward bucket for the community staking pool.
    RewardBucket communityBase;
    /// @notice The reward bucket for the delegated rewards.
    RewardBucket operatorDelegated;
  }

  /// @notice This struct is used to store the vault config.
  struct VaultConfig {
    /// @notice The delegation rate expressed in basis points. For example, a delegation rate of
    /// 4.5% would be represented as 450 basis points.
    uint32 delegationRate;
    /// @notice Flag that signals if the reward vault is open
    bool isOpen;
  }

  /// @notice This struct is used to store the checkpoint information at the time the reward vault
  /// is closed
  struct VestingCheckpointData {
    /// @notice The total staked LINK amount of the operator staking pool at the time
    /// the reward vault was closed
    uint256 operatorPoolTotalPrincipal;
    /// @notice The total staked LINK amount of the community staking pool at the time
    /// the reward vault was closed
    uint256 communityPoolTotalPrincipal;
    /// @notice The block number of at the time the reward vault was migrated or closed
    uint256 finalBlockNumber;
  }

  /// @notice This struct is used for aggregating the return values of a function that calculates
  /// the reward aggregate reward rate splits.
  struct BucketRewardEmissionSplit {
    /// @notice The reward for the community staking pool
    uint256 communityReward;
    /// @notice The reward for the operator staking pool
    uint256 operatorReward;
    /// @notice The reward for the delegated staking pool
    uint256 operatorDelegatedReward;
    /// @notice The aggregate reward rate for the community staking pool
    uint256 communityRate;
    /// @notice The aggregate reward rate for the operator staking pool
    uint256 operatorRate;
    /// @notice The aggregate reward rate for the delegated staking pool
    uint256 delegatedRate;
  }

  /// @notice This is the ID for the rewarder role, which is given to the
  /// addresses that will add rewards to the vault.
  /// @dev Hash: beec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f6
  bytes32 public constant REWARDER_ROLE = keccak256("REWARDER_ROLE");
  /// @notice The maximum possible value of a multiplier. Current implementation requires that this
  /// value is 1e18 (i.e. 100%).
  uint256 private constant MAX_MULTIPLIER = 1e18;
  /// @notice The denominator used to calculate the delegation rate.
  uint256 private constant DELEGATION_BASIS_POINTS_DENOMINATOR = 10000;
  /// @notice The multiplier ramp up period duration in seconds.
  uint256 private immutable i_multiplierDuration;
  /// @notice The LINK token
  LinkTokenInterface private immutable i_LINK;
  /// @notice The community staking pool.
  CommunityStakingPool private immutable i_communityStakingPool;
  /// @notice The operator staking pool.
  OperatorStakingPool private immutable i_operatorStakingPool;
  /// @notice The reward buckets.
  RewardBuckets private s_rewardBuckets;
  /// @notice The vault config.
  VaultConfig private s_vaultConfig;
  /// @notice The checkpoint information at the time the reward vault was closed
  VestingCheckpointData private s_finalVestingCheckpointData;
  /// @notice The packed timestamps of reward updates. First digits contain community reward
  /// update timestamp and last 18 digits contain operator timestamp, e.g., if both timestamps are
  /// 1_697_127_483_832 then the value would be 1_697_127_483_832_000_001_697_127_483_832.
  uint256 private s_packedRewardUpdateTimestamps;
  /// @notice Stores reward information for each staker
  mapping(address => StakerReward) private s_rewards;

  constructor(ConstructorParams memory params)
    PausableWithAccessControl(params.adminRoleTransferDelay, msg.sender)
  {
    if (address(params.linkToken) == address(0)) revert InvalidZeroAddress();
    if (address(params.communityStakingPool) == address(0)) revert InvalidZeroAddress();
    if (address(params.operatorStakingPool) == address(0)) revert InvalidZeroAddress();
    if (params.delegationRate > DELEGATION_BASIS_POINTS_DENOMINATOR) revert InvalidDelegationRate();

    i_multiplierDuration = params.multiplierDuration;
    i_LINK = params.linkToken;
    i_communityStakingPool = params.communityStakingPool;
    i_operatorStakingPool = params.operatorStakingPool;

    s_vaultConfig.delegationRate = params.delegationRate;
    emit DelegationRateSet(0, params.delegationRate);

    s_vaultConfig.isOpen = true;
    emit VaultOpened();
  }

  /// @notice Adds more rewards into the reward vault
  /// Calculates the reward duration from the amount and aggregate reward rate
  /// @dev To add rewards to all pools use address(0) as the pool address
  /// @dev There is a possibility that a fraction of the added rewards can be locked in this
  /// contract as dust, specifically, when the amount is not divided by the aggregate reward rate
  /// evenly. We
  /// will handle this case operationally and make sure that the amount is large relative to the
  /// aggregate reward rate so there will only be small dust (less than 10^18 juels).
  /// @param pool The staking pool address
  /// @param amount The reward amount
  /// @param emissionRate The target aggregate reward rate (token/second)
  /// @dev precondition The caller must have the REWARDER role.
  /// @dev precondition This contract must be open and not paused.
  /// @dev precondition The caller must have at least `amount` LINK tokens.
  /// @dev precondition The caller must have approved this contract for the transfer of at least
  /// `amount` LINK tokens.
  function addReward(
    address pool,
    uint256 amount,
    uint256 emissionRate
  ) external onlyRewarder whenOpen whenNotPaused {
    // check if the pool is either community staking pool or operator staking pool
    // if the pool is the zero address, then the reward is split between all pools
    if (
      pool != address(0) && pool != address(i_communityStakingPool)
        && pool != address(i_operatorStakingPool)
    ) {
      revert InvalidPool();
    }
    // check that the aggregate reward rate is greater than zero
    if (emissionRate == 0) revert InvalidEmissionRate();

    // update the reward per tokens
    _updateRewardPerToken();

    // update the reward buckets
    _updateRewardBuckets({pool: pool, amount: amount, emissionRate: emissionRate});

    // transfer the reward tokens to the reward vault
    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transferFrom({from: msg.sender, to: address(this), value: amount});

    emit RewardAdded(pool, amount, emissionRate);
  }

  /// @notice Returns the delegation rate
  /// @return The delegation rate expressed in basis points
  function getDelegationRate() external view returns (uint256) {
    return s_vaultConfig.delegationRate;
  }

  /// @notice Updates the delegation rate
  /// @param newDelegationRate The delegation rate.
  /// @dev precondition The caller must have the default admin role.
  function setDelegationRate(uint256 newDelegationRate) external onlyRole(DEFAULT_ADMIN_ROLE) {
    uint256 oldDelegationRate = s_vaultConfig.delegationRate;
    if (
      oldDelegationRate == newDelegationRate
        || newDelegationRate > DELEGATION_BASIS_POINTS_DENOMINATOR
    ) {
      revert InvalidDelegationRate();
    }

    uint256 communityRateWithoutDelegation =
      s_rewardBuckets.communityBase.emissionRate + s_rewardBuckets.operatorDelegated.emissionRate;

    uint256 delegatedRate = newDelegationRate == 0
      ? 0
      : communityRateWithoutDelegation * newDelegationRate / DELEGATION_BASIS_POINTS_DENOMINATOR;

    if (delegatedRate == 0 && newDelegationRate != 0 && communityRateWithoutDelegation != 0) {
      // delegated rate has rounded down to zero
      revert InsufficentRewardsForDelegationRate();
    }

    _updateRewardPerToken();

    uint256 unvestedRewards = _getUnvestedRewards(s_rewardBuckets.communityBase)
      + _getUnvestedRewards(s_rewardBuckets.operatorDelegated);
    uint256 communityRate = communityRateWithoutDelegation - delegatedRate;
    s_rewardBuckets.communityBase.emissionRate = communityRate.toUint80();
    s_rewardBuckets.operatorDelegated.emissionRate = delegatedRate.toUint80();

    // NOTE - the reward duration for both buckets need to be in sync.
    if (newDelegationRate == 0) {
      delete s_rewardBuckets.operatorDelegated.rewardDurationEndsAt;
      _updateRewardDurationEndsAt({
        bucket: s_rewardBuckets.communityBase,
        rewardAmount: unvestedRewards,
        emissionRate: communityRate
      });
    } else if (newDelegationRate == DELEGATION_BASIS_POINTS_DENOMINATOR) {
      delete s_rewardBuckets.communityBase.rewardDurationEndsAt;
      _updateRewardDurationEndsAt({
        bucket: s_rewardBuckets.operatorDelegated,
        rewardAmount: unvestedRewards,
        emissionRate: delegatedRate
      });
    } else if (unvestedRewards != 0) {
      uint256 delegatedRewards =
        unvestedRewards * newDelegationRate / DELEGATION_BASIS_POINTS_DENOMINATOR;
      uint256 communityRewards = unvestedRewards - delegatedRewards;
      _updateRewardDurationEndsAt({
        bucket: s_rewardBuckets.communityBase,
        rewardAmount: communityRewards,
        emissionRate: communityRate
      });
      _updateRewardDurationEndsAt({
        bucket: s_rewardBuckets.operatorDelegated,
        rewardAmount: delegatedRewards,
        emissionRate: delegatedRate
      });
    }

    s_vaultConfig.delegationRate = newDelegationRate.toUint32();

    emit DelegationRateSet(oldDelegationRate, newDelegationRate);
  }

  // =================
  // IRewardVault
  // =================

  /// @inheritdoc IRewardVault
  /// @dev precondition This contract must not be paused.
  /// @dev precondition The caller must be a staker with a non-zero reward.
  function claimReward() external whenNotPaused returns (uint256) {
    bool isOperator = _isOperator(msg.sender);

    _updateRewardPerToken(isOperator ? StakerType.OPERATOR : StakerType.COMMUNITY);

    IStakingPool stakingPool =
      isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool);
    uint256 stakerPrincipal = _getStakerPrincipal(msg.sender, stakingPool);
    StakerReward memory stakerReward = _calculateStakerReward({
      staker: msg.sender,
      isOperator: isOperator,
      stakerPrincipal: stakerPrincipal
    });

    uint112 newVestedBaseRewards = _calculateNewVestedBaseRewards(
      stakerReward, _getMultiplier(_getStakerStakedAtTime(msg.sender, stakingPool))
    );

    stakerReward.unvestedBaseReward -= newVestedBaseRewards;
    stakerReward.claimedBaseRewardsInPeriod += newVestedBaseRewards;

    uint256 newVestedRewards = stakerReward.vestedBaseReward + newVestedBaseRewards;
    delete stakerReward.vestedBaseReward;

    if (isOperator) {
      newVestedRewards += stakerReward.vestedDelegatedReward;
      delete stakerReward.vestedDelegatedReward;
    }

    if (newVestedRewards == 0) {
      revert NoRewardToClaim();
    }

    s_rewards[msg.sender] = stakerReward;

    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transfer(msg.sender, newVestedRewards);

    emit RewardClaimed(msg.sender, newVestedRewards);
    emit StakerRewardUpdated(
      msg.sender,
      0,
      0,
      stakerReward.baseRewardPerToken,
      stakerReward.operatorDelegatedRewardPerToken,
      stakerReward.claimedBaseRewardsInPeriod
    );

    return newVestedRewards;
  }

  /// @inheritdoc IRewardVault
  /// @dev precondition The caller must be a staking pool.
  function updateReward(address staker, uint256 stakerPrincipal) external onlyStakingPool {
    _updateRewardPerToken();

    StakerReward memory stakerReward = _calculateStakerReward({
      staker: staker,
      isOperator: msg.sender == address(i_operatorStakingPool),
      stakerPrincipal: stakerPrincipal
    });
    s_rewards[staker] = stakerReward;

    emit StakerRewardUpdated(
      staker,
      stakerReward.vestedBaseReward,
      stakerReward.vestedDelegatedReward,
      stakerReward.baseRewardPerToken,
      stakerReward.operatorDelegatedRewardPerToken,
      stakerReward.claimedBaseRewardsInPeriod
    );
  }

  /// @inheritdoc IRewardVault
  /// @dev This applies any final logic such as the multipliers to the staker's newly accrued and
  /// stored rewards and store the value.
  /// @dev The caller staking pool must update the total staked LINK amount of the pool AFTER
  /// calling this
  /// function.
  /// @dev precondition The caller must be a staking pool.
  function concludeRewardPeriod(
    address staker,
    uint256 oldPrincipal,
    uint256 stakedAt,
    uint256 unstakedAmount,
    bool shouldForfeit
  ) external onlyStakingPool {
    // _isOperator is not used here to save gas.  The _isOperator function
    // currently checks for 2 things.  The first that the staker is currently
    // an operator and the other is that the staker is a removed operator.  As
    // this function will only be called by a staking pool, the contract can
    // safely assume that the staker is an operator if the msg.sender is the
    // operator staking pool as upgrading a pool/reward vault means that the operator
    // staking pool will point to a new reward vault.  Additionally the contract
    // assumes that it does not need to do the second check to determine whether
    // or not an operator had been removed as it is unlikely that an operator
    // is removed after the reward vault is closed.
    bool isOperator = msg.sender == address(i_operatorStakingPool);

    _updateRewardPerToken(isOperator ? StakerType.OPERATOR : StakerType.COMMUNITY);

    StakerReward memory stakerReward = _calculateStakerReward({
      staker: staker,
      isOperator: isOperator,
      stakerPrincipal: oldPrincipal
    });

    uint112 newVestedBaseRewards =
      _calculateNewVestedBaseRewards(stakerReward, _getMultiplier(stakedAt));

    stakerReward.unvestedBaseReward -= newVestedBaseRewards;
    stakerReward.vestedBaseReward += newVestedBaseRewards;

    // claimedBaseRewardsInPeriod is reset as this function ends a
    // reward period for the staker.  This variable only tracks the amount
    // of rewards a staker has claimed within a period hence should only
    // accumulate from zero after this function is called.
    delete stakerReward.claimedBaseRewardsInPeriod;

    if (!shouldForfeit) {
      return _storeRewardAndEmitEvents(staker, stakerReward, shouldForfeit);
    }

    uint112 unvestedRewardAmount = stakerReward.unvestedBaseReward;

    // The function terminates here as a staker that has reached the maximum
    // multiplier will not have any unvested rewards hence will not forfeit
    // anything.
    if (unvestedRewardAmount == 0) {
      return _storeRewardAndEmitEvents(staker, stakerReward, shouldForfeit);
    }

    IStakingPool stakingPool =
      isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool);

    uint256 remainingPoolPrincipal = _getTotalPrincipal(stakingPool) - oldPrincipal;

    // This is the case when the last staker exits the pool.
    if (remainingPoolPrincipal == 0) {
      delete stakerReward.unvestedBaseReward;
      stakerReward.vestedBaseReward += unvestedRewardAmount;
      emit ForfeitedRewardDistributed(0, 0, unvestedRewardAmount, isOperator);
      return _storeRewardAndEmitEvents(staker, stakerReward, shouldForfeit);
    }

    // This handles an edge case when an operator with 0 principal remaining (due to
    // slashing) gets removed and forfeits rewards. In this scenario, the reward vault will
    // forfeit the full amount of unclaimable rewards instead of calculating
    // the proportion of the unclaimable rewards that should be forfeited.
    // There is another case when forfeitedRewardAmount rounds down to 0, which is when a staker has
    // earned too little rewards and unstakes a very small amount. In this case, we do not forfeit
    // any rewards.
    uint256 forfeitedRewardAmount = oldPrincipal == 0
      ? unvestedRewardAmount
      : unvestedRewardAmount * unstakedAmount / oldPrincipal;

    RewardBucket storage rewardBucket =
      isOperator ? s_rewardBuckets.operatorBase : s_rewardBuckets.communityBase;

    uint256 redistributedRewardPerToken = forfeitedRewardAmount.divWadDown(remainingPoolPrincipal);

    /// There is an extreme edge case where redistributedRewardPerToken may overflow
    /// because the remaining principal in a pool is an extremely small amount.
    /// This scenario is however extremely unlikely because there is a minimum
    /// staked amount for both the operator and community staking pools.
    /// Operators may be slashed so that the sum of remaining staked amounts
    /// is extremely small but this scenario is also unlikely to happen as
    /// it would mean multiple CL services going down at the same time.
    rewardBucket.vestedRewardPerToken += redistributedRewardPerToken.toUint80();

    emit ForfeitedRewardDistributed(
      forfeitedRewardAmount, redistributedRewardPerToken, 0, isOperator
    );

    // Update stakerRewardPerToken so that the staker doesn't benefit from redistributed
    // tokens
    _updateStakerRewardPerToken(stakerReward, isOperator);

    stakerReward.unvestedBaseReward -= forfeitedRewardAmount.toUint112();

    return _storeRewardAndEmitEvents(staker, stakerReward, shouldForfeit);
  }

  /// @notice Updates a staker's reward data and emits events
  /// @param staker The address of the staker to update reward data for
  /// @param stakerReward The staker's new reward data
  /// @param shouldForfeit True if the staker has forfeited some unvested
  /// rewards
  function _storeRewardAndEmitEvents(
    address staker,
    StakerReward memory stakerReward,
    bool shouldForfeit
  ) internal {
    s_rewards[staker] = stakerReward;

    emit RewardFinalized(staker, shouldForfeit);
    emit StakerRewardUpdated(
      staker,
      stakerReward.vestedBaseReward,
      stakerReward.vestedDelegatedReward,
      stakerReward.baseRewardPerToken,
      stakerReward.operatorDelegatedRewardPerToken,
      stakerReward.claimedBaseRewardsInPeriod
    );
  }

  /// @notice Calculates new vested base rewards, taking into account the multiplier
  /// and the rewards that have already been claimed.
  /// @return New vested base rewards
  function _calculateNewVestedBaseRewards(
    StakerReward memory stakerReward,
    uint256 multiplier
  ) internal pure returns (uint112) {
    return uint256(stakerReward.unvestedBaseReward + stakerReward.claimedBaseRewardsInPeriod)
      .mulWadDown(multiplier).toUint112() - stakerReward.claimedBaseRewardsInPeriod;
  }

  /// @inheritdoc IRewardVault
  /// @dev Withdraws any unvested LINK rewards to the owner's address.
  /// @dev precondition The caller must have the default admin role.
  /// @dev precondition This contract must be open.
  function close() external onlyRole(DEFAULT_ADMIN_ROLE) whenOpen {
    (, uint256 totalUnvestedRewards,,,) = _stopVestingRewardsToBuckets();
    delete s_vaultConfig.isOpen;
    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transfer(msg.sender, totalUnvestedRewards);
    emit VaultClosed(totalUnvestedRewards);
  }

  /// @inheritdoc IRewardVault
  function getReward(address staker) external view returns (uint256) {
    // Determine if staker is operator or community
    bool isOperator = _isOperator(staker);

    IStakingPool stakingPool =
      isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool);

    uint256 stakerPrincipal = _getStakerPrincipal(staker, stakingPool);

    (StakerReward memory stakerReward, uint256 forfeitedReward) =
      _getReward(staker, stakerPrincipal, isOperator);

    (,, uint256 reclaimableReward) = _calculateForfeitedRewardDistribution(
      forfeitedReward, _getTotalPrincipal(stakingPool) - stakerPrincipal
    );

    return stakerReward.vestedBaseReward + stakerReward.vestedDelegatedReward + reclaimableReward;
  }

  /// @inheritdoc IRewardVault
  function isOpen() external view returns (bool) {
    return s_vaultConfig.isOpen;
  }

  /// @inheritdoc IRewardVault
  function hasRewardDurationEnded(address stakingPool) external view returns (bool) {
    if (stakingPool == address(i_operatorStakingPool)) {
      return s_rewardBuckets.operatorBase.rewardDurationEndsAt <= block.timestamp
        && s_rewardBuckets.operatorDelegated.rewardDurationEndsAt <= block.timestamp;
    }
    if (stakingPool == address(i_communityStakingPool)) {
      return s_rewardBuckets.communityBase.rewardDurationEndsAt <= block.timestamp;
    }

    revert InvalidPool();
  }

  /// @inheritdoc IRewardVault
  function hasRewardAdded() external view returns (bool) {
    return s_rewardBuckets.operatorBase.emissionRate != 0
      || s_rewardBuckets.communityBase.emissionRate != 0
      || s_rewardBuckets.operatorDelegated.emissionRate != 0;
  }

  /// @inheritdoc IRewardVault
  function getStoredReward(address staker) external view returns (StakerReward memory) {
    return s_rewards[staker];
  }

  /// @notice Returns the reward buckets within this vault
  /// @return The reward buckets
  function getRewardBuckets() external view returns (RewardBuckets memory) {
    return s_rewardBuckets;
  }

  /// @notice Returns the timestamp of the last reward per token update
  /// @return uint256 communityRewardUpdateTimestamp The timestamp of the last update
  /// @return uint256 operatorRewardUpdateTimestamp The timestamp of the last update
  function getRewardPerTokenUpdatedAt() external view returns (uint256, uint256) {
    return _getRewardUpdateTimestamps(s_packedRewardUpdateTimestamps);
  }

  /// @notice Returns the multiplier ramp up time
  /// @return uint256 The multiplier ramp up time
  function getMultiplierDuration() external view returns (uint256) {
    return i_multiplierDuration;
  }

  /// @notice Returns the ramp up multiplier of the staker
  /// @dev Multipliers are in the range of 0 and 1, so we multiply them by 1e18 (WAD) to preserve
  /// the decimals.
  /// @param staker The address of the staker
  /// @return uint256 The staker's multiplier
  function getMultiplier(address staker) external view returns (uint256) {
    IStakingPool stakingPool = _isOperator(staker)
      ? IStakingPool(i_operatorStakingPool)
      : IStakingPool(i_communityStakingPool);

    return _getMultiplier(_getStakerStakedAtTime(staker, stakingPool));
  }

  /// @notice Calculates and returns the latest reward info of the staker
  /// @param staker The staker address
  /// @return StakerReward The staker's reward info
  /// @return uint256 The staker's forfeited reward in juels
  function calculateLatestStakerReward(address staker)
    external
    view
    returns (StakerReward memory, uint256)
  {
    // Determine if staker is operator or community
    bool isOperator = _isOperator(staker);

    IStakingPool stakingPool =
      isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool);

    uint256 stakerPrincipal = _getStakerPrincipal(staker, stakingPool);
    return _getReward(staker, stakerPrincipal, isOperator);
  }

  /// @notice Returns the final checkpoint data
  /// @return VestingCheckpointData The final checkpoint
  function getFinalVestingCheckpointData() external view returns (VestingCheckpointData memory) {
    return s_finalVestingCheckpointData;
  }

  /// @notice Returns the unvested rewards
  /// @return unvestedCommunityBaseRewards The unvested community base rewards
  /// @return unvestedOperatorBaseRewards The unvested operator base rewards
  /// @return unvestedOperatorDelegatedRewards The unvested operator delegated rewards
  function getUnvestedRewards() external view returns (uint256, uint256, uint256) {
    uint256 unvestedCommunityBaseRewards = _getUnvestedRewards(s_rewardBuckets.communityBase);
    uint256 unvestedOperatorBaseRewards = _getUnvestedRewards(s_rewardBuckets.operatorBase);
    uint256 unvestedOperatorDelegatedRewards =
      _getUnvestedRewards(s_rewardBuckets.operatorDelegated);
    return
      (unvestedCommunityBaseRewards, unvestedOperatorBaseRewards, unvestedOperatorDelegatedRewards);
  }

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

  /// @inheritdoc IRewardVault
  function getStakingPools() external view override returns (address[] memory) {
    address[] memory stakingPools = new address[](2);
    stakingPools[0] = address(i_operatorStakingPool);
    stakingPools[1] = address(i_communityStakingPool);
    return stakingPools;
  }

  // =========
  // Helpers
  // =========

  /// @notice Stops rewards in all buckets from vesting and close the vault.
  /// @dev This will also checkpoint the staking pools
  /// @return uint256 The total aggregate reward rate from all three buckets
  /// @return uint256 The total amount of available rewards in juels
  /// @return uint256 The amount of available operator base rewards in juels
  /// @return uint256 The amount of available community base rewards in juels
  /// @return uint256 The amount of available operator delegated rewards in juels
  function _stopVestingRewardsToBuckets()
    private
    returns (uint256, uint256, uint256, uint256, uint256)
  {
    _updateRewardPerToken();

    uint256 unvestedOperatorBaseRewards = _stopVestingBucketRewards(s_rewardBuckets.operatorBase);
    uint256 unvestedCommunityBaseRewards = _stopVestingBucketRewards(s_rewardBuckets.communityBase);
    uint256 unvestedOperatorDelegatedRewards =
      _stopVestingBucketRewards(s_rewardBuckets.operatorDelegated);
    uint256 totalUnvestedRewards =
      unvestedOperatorBaseRewards + unvestedCommunityBaseRewards + unvestedOperatorDelegatedRewards;

    _checkpointStakingPools();

    return (
      s_rewardBuckets.operatorBase.emissionRate + s_rewardBuckets.communityBase.emissionRate
        + s_rewardBuckets.operatorDelegated.emissionRate,
      totalUnvestedRewards,
      unvestedOperatorBaseRewards,
      unvestedCommunityBaseRewards,
      unvestedOperatorDelegatedRewards
    );
  }

  /// @notice Returns the total staked LINK amount staked in a staking pool.  This will
  /// return the staking pool's latest total staked LINK amount if the vault has not been
  /// closed and the pool's total staked LINK amount at the time the vault was
  /// closed if the vault has already been closed.
  /// @param stakingPool The staking pool to query the total staked LINK amount for
  /// @return uint256 The total staked LINK amount staked in the staking pool
  function _getTotalPrincipal(IStakingPool stakingPool) private view returns (uint256) {
    return s_vaultConfig.isOpen
      ? stakingPool.getTotalPrincipal()
      : _getFinalTotalPoolPrincipal(stakingPool);
  }

  /// @notice Returns the staker's staked LINK amount in a staking pool.  This will
  /// return the staker's latest staked LINK amount if the vault has not been
  /// closed and the staker's staked LINK amount at the time the vault was
  /// closed if the vault has already been closed.
  /// @param staker The staker to query the total staked LINK amount for
  /// @param stakingPool The staking pool to query the total staked LINK amount for
  /// @return uint256 The staker's staked LINK amount in the staking pool in juels
  function _getStakerPrincipal(
    address staker,
    IStakingPool stakingPool
  ) private view returns (uint256) {
    return s_vaultConfig.isOpen
      ? stakingPool.getStakerPrincipal(staker)
      : stakingPool.getStakerPrincipalAt(staker, s_finalVestingCheckpointData.finalBlockNumber);
  }

  /// @notice Helper function to get a staker's current multiplier
  /// @param stakedAt The time the staker last staked at
  /// @return uint256 The staker's multiplier
  function _getMultiplier(uint256 stakedAt) private view returns (uint256) {
    if (stakedAt == 0) return 0;

    if (!s_vaultConfig.isOpen) return MAX_MULTIPLIER;

    uint256 multiplierDuration = i_multiplierDuration;
    if (multiplierDuration == 0) return MAX_MULTIPLIER;

    return Math.min(
      FixedPointMathLib.divWadDown(block.timestamp - stakedAt, multiplierDuration), MAX_MULTIPLIER
    );
  }

  /// @notice Returns the staker's staked at time in a staking pool.  This will
  /// return the staker's latest staked at time if the vault has not been
  /// closed and the staker's staked at time at the time the vault was
  /// closed if the vault has already been closed.
  /// @param staker The staker to query the staked at time for
  /// @param stakingPool The staking pool to query the staked at time for
  /// @return uint256 The staker's average staked at time in the staking pool
  function _getStakerStakedAtTime(
    address staker,
    IStakingPool stakingPool
  ) private view returns (uint256) {
    return s_vaultConfig.isOpen
      ? stakingPool.getStakerStakedAtTime(staker)
      : stakingPool.getStakerStakedAtTimeAt(staker, s_finalVestingCheckpointData.finalBlockNumber);
  }

  /// @notice Return the staking pool's total staked LINK amount at the time the vault was
  /// closed
  /// @param stakingPool The staking pool to query the total staked LINK amount for
  /// @return uint256 The pool's total staked LINK amount at the time the vault was
  /// closed
  function _getFinalTotalPoolPrincipal(IStakingPool stakingPool) private view returns (uint256) {
    return address(stakingPool) == address(i_operatorStakingPool)
      ? s_finalVestingCheckpointData.operatorPoolTotalPrincipal
      : s_finalVestingCheckpointData.communityPoolTotalPrincipal;
  }

  /// @notice Records the final block number and the total staked LINK amounts
  /// in the operator and community staking pools
  function _checkpointStakingPools() private {
    s_finalVestingCheckpointData.operatorPoolTotalPrincipal =
      i_operatorStakingPool.getTotalPrincipal();
    s_finalVestingCheckpointData.communityPoolTotalPrincipal =
      i_communityStakingPool.getTotalPrincipal();
    s_finalVestingCheckpointData.finalBlockNumber = block.number;
  }

  /// @notice Stops rewards in a bucket from vesting
  /// @param bucket The bucket to stop vesting rewards for
  /// @return uint256 The amount of unvested rewards in juels
  function _stopVestingBucketRewards(RewardBucket storage bucket) private returns (uint256) {
    uint256 unvestedRewards = _getUnvestedRewards(bucket);
    bucket.rewardDurationEndsAt = block.timestamp.toUint80();
    return unvestedRewards;
  }

  /// @notice Updates the reward buckets
  /// @param pool The staking pool address
  /// @param amount The reward amount
  /// @param emissionRate The target aggregate reward rate (Juels/second)
  function _updateRewardBuckets(address pool, uint256 amount, uint256 emissionRate) private {
    // split the reward and aggregate reward rate for the different reward buckets
    BucketRewardEmissionSplit memory emissionSplitData = _getBucketRewardAndEmissionRateSplit({
      pool: pool,
      amount: amount,
      emissionRate: emissionRate,
      isDelegated: s_vaultConfig.delegationRate != 0
    });

    // If the aggregate reward rate is zero, we don't update the reward bucket
    // This is because we do not allow a zero aggregate reward rate
    // A zero aggregate reward rate means no rewards have been added
    if (emissionSplitData.communityRate != 0) {
      _updateRewardBucket({
        bucket: s_rewardBuckets.communityBase,
        amount: emissionSplitData.communityReward,
        emissionRate: emissionSplitData.communityRate
      });
    }
    if (emissionSplitData.operatorRate != 0) {
      _updateRewardBucket({
        bucket: s_rewardBuckets.operatorBase,
        amount: emissionSplitData.operatorReward,
        emissionRate: emissionSplitData.operatorRate
      });
    }
    if (emissionSplitData.delegatedRate != 0) {
      _updateRewardBucket({
        bucket: s_rewardBuckets.operatorDelegated,
        amount: emissionSplitData.operatorDelegatedReward,
        emissionRate: emissionSplitData.delegatedRate
      });
    }
  }

  /// @notice Updates the reward bucket
  /// @param bucket The reward bucket
  /// @param amount The reward amount
  /// @param emissionRate The target aggregate reward rate (token/second)
  function _updateRewardBucket(
    RewardBucket storage bucket,
    uint256 amount,
    uint256 emissionRate
  ) private {
    // calculate the remaining rewards
    uint256 remainingRewards = _getUnvestedRewards(bucket);

    // if the amount of rewards is less than what becomes available per second, we revert
    if (amount + remainingRewards < emissionRate) revert RewardDurationTooShort();

    _updateRewardDurationEndsAt({
      bucket: bucket,
      rewardAmount: amount + remainingRewards,
      emissionRate: emissionRate
    });
    bucket.emissionRate = emissionRate.toUint80();
  }

  /// @notice Updates the reward duration end time of the bucket
  /// @param bucket The reward bucket
  /// @param rewardAmount The reward amount
  /// @param emissionRate The aggregate reward rate
  function _updateRewardDurationEndsAt(
    RewardBucket storage bucket,
    uint256 rewardAmount,
    uint256 emissionRate
  ) private {
    if (emissionRate == 0) return;
    bucket.rewardDurationEndsAt = (block.timestamp + (rewardAmount / emissionRate)).toUint80();
  }

  /// @notice Splits the reward and aggregate reward rates between the different reward buckets
  /// @dev If the pool is not targeted, the returned reward and aggregate reward rate will be zero
  /// @param pool The staking pool address (or zero address if the reward is split between all
  /// pools)
  /// @param amount The reward amount
  /// @param emissionRate The aggregate reward rate (juels/second)
  /// @param isDelegated Whether the reward is delegated or not
  /// @return BucketRewardEmissionSplit The rewards and aggregate reward rates after
  /// distributing the reward amount to the buckets
  function _getBucketRewardAndEmissionRateSplit(
    address pool,
    uint256 amount,
    uint256 emissionRate,
    bool isDelegated
  ) private view returns (BucketRewardEmissionSplit memory) {
    // when splitting reward and rate, a pool's share is 0 if it is not targeted by the pool
    // address,
    // otherwise it is the pool's max size
    // a pool's share is used to split rewards and aggregate reward rates proportionally
    uint256 communityPoolShare =
      pool != address(i_operatorStakingPool) ? i_communityStakingPool.getMaxPoolSize() : 0;
    uint256 operatorPoolShare =
      pool != address(i_communityStakingPool) ? i_operatorStakingPool.getMaxPoolSize() : 0;
    uint256 totalPoolShare = communityPoolShare + operatorPoolShare;

    uint256 operatorReward;
    uint256 communityReward;
    uint256 operatorRate;
    uint256 communityRate;
    if (pool == address(i_operatorStakingPool)) {
      operatorReward = amount;
      operatorRate = emissionRate;
    } else if (pool == address(i_communityStakingPool)) {
      communityReward = amount;
      communityRate = emissionRate;
    } else {
      // prevent a possible rounding to zero error by validating inputs
      _checkForRoundingToZeroRewardAmountSplit({
        rewardAmount: amount,
        operatorPoolShare: operatorPoolShare,
        totalPoolShare: totalPoolShare
      });
      _checkForRoundingToZeroEmissionRateSplit({
        emissionRate: emissionRate,
        operatorPoolShare: operatorPoolShare,
        totalPoolShare: totalPoolShare
      });

      operatorReward = amount * operatorPoolShare / totalPoolShare;
      operatorRate = emissionRate * operatorPoolShare / totalPoolShare;

      communityReward = amount - operatorReward;
      communityRate = emissionRate - operatorRate;
    }

    uint256 operatorDelegatedReward;
    uint256 delegatedRate;
    // if there is no delegation or the community pool is not targeted, the delegated reward and
    // rate is zero
    if (isDelegated && communityPoolShare != 0) {
      // calculate the delegated pool reward and remove from community reward
      operatorDelegatedReward =
        communityReward * s_vaultConfig.delegationRate / DELEGATION_BASIS_POINTS_DENOMINATOR;
      if (communityReward > 0 && operatorDelegatedReward == 0) revert InvalidRewardAmount();
      communityReward -= operatorDelegatedReward;

      // calculate the delegated pool aggregate reward rate and remove from community rate
      delegatedRate =
        communityRate * s_vaultConfig.delegationRate / DELEGATION_BASIS_POINTS_DENOMINATOR;
      if (communityRate > 0 && delegatedRate == 0) revert InvalidEmissionRate();
      communityRate -= delegatedRate;
    }

    return (
      BucketRewardEmissionSplit({
        communityReward: communityReward,
        operatorReward: operatorReward,
        operatorDelegatedReward: operatorDelegatedReward,
        communityRate: communityRate,
        operatorRate: operatorRate,
        delegatedRate: delegatedRate
      })
    );
  }

  /// @notice Validates the added reward amount after splitting to avoid a rounding error when
  /// dividing
  /// @param rewardAmount The reward amount
  /// @param operatorPoolShare The size of the operator staking pool to take into account
  /// @param totalPoolShare The total size of the pools to take into account
  function _checkForRoundingToZeroRewardAmountSplit(
    uint256 rewardAmount,
    uint256 operatorPoolShare,
    uint256 totalPoolShare
  ) private pure {
    if (
      rewardAmount != 0
        && ((operatorPoolShare != 0 && rewardAmount * operatorPoolShare < totalPoolShare))
    ) {
      revert InvalidRewardAmount();
    }
  }

  /// @notice Validates the aggregate reward rate after splitting to avoid a rounding error when
  /// dividing
  /// @param emissionRate The aggregate reward rate
  /// @param operatorPoolShare The size of the operator staking pool to take into account
  /// @param totalPoolShare The total size of the pools to take into account
  function _checkForRoundingToZeroEmissionRateSplit(
    uint256 emissionRate,
    uint256 operatorPoolShare,
    uint256 totalPoolShare
  ) private pure {
    if ((operatorPoolShare != 0 && emissionRate * operatorPoolShare < totalPoolShare)) {
      revert InvalidEmissionRate();
    }
  }

  /// @notice Private util function to unpack and return reward update timestamps.
  /// @return uint256 communityRewardUpdateTimestamp
  /// @return uint256 operatorRewardUpdateTimestamp
  function _getRewardUpdateTimestamps(uint256 packedRewardUpdateTimestamps)
    private
    pure
    returns (uint256, uint256)
  {
    uint256 communityRewardUpdateTimestamp = packedRewardUpdateTimestamps / 1e18;
    uint256 operatorRewardUpdateTimestamp = packedRewardUpdateTimestamps % 1e18;

    return (communityRewardUpdateTimestamp, operatorRewardUpdateTimestamp);
  }

  /// @notice Private util function to pack and set reward update timestamps.
  function _setRewardUpdateTimestamps(
    uint256 communityRewardUpdateTimestamp,
    uint256 operatorRewardUpdateTimestamp
  ) private {
    s_packedRewardUpdateTimestamps =
      communityRewardUpdateTimestamp * 1e18 + operatorRewardUpdateTimestamp;
  }

  /// @notice Private util function for updateRewardPerToken
  function _updateRewardPerToken() private {
    (uint256 communityRewardUpdateTimestamp, uint256 operatorRewardUpdateTimestamp) =
      _getRewardUpdateTimestamps(s_packedRewardUpdateTimestamps);

    if (
      communityRewardUpdateTimestamp == block.timestamp
        && operatorRewardUpdateTimestamp == block.timestamp
    ) {
      // if the pools were previously updated in the same block there is no recalculation of reward
      return;
    }

    (
      uint256 communityRewardPerToken,
      uint256 operatorRewardPerToken,
      uint256 operatorDelegatedRewardPerToken
    ) = _calculatePoolsRewardPerToken();

    s_rewardBuckets.communityBase.vestedRewardPerToken = communityRewardPerToken.toUint80();
    s_rewardBuckets.operatorBase.vestedRewardPerToken = operatorRewardPerToken.toUint80();
    s_rewardBuckets.operatorDelegated.vestedRewardPerToken =
      operatorDelegatedRewardPerToken.toUint80();

    _setRewardUpdateTimestamps(block.timestamp, block.timestamp);
    emit CommunityPoolRewardUpdated(communityRewardPerToken);
    emit OperatorPoolRewardUpdated(operatorRewardPerToken, operatorDelegatedRewardPerToken);
  }

  /// @notice Private util function for updateRewardPerToken
  /// @param stakerType The staker type to update the reward for.
  function _updateRewardPerToken(StakerType stakerType) private {
    (uint256 communityRewardUpdateTimestamp, uint256 operatorRewardUpdateTimestamp) =
      _getRewardUpdateTimestamps(s_packedRewardUpdateTimestamps);

    if (stakerType == StakerType.COMMUNITY) {
      if (communityRewardUpdateTimestamp == block.timestamp) {
        return;
      }

      s_rewardBuckets.communityBase.vestedRewardPerToken = _calculateVestedRewardPerToken(
        s_rewardBuckets.communityBase,
        _getTotalPrincipal(i_communityStakingPool),
        communityRewardUpdateTimestamp
      ).toUint80();

      _setRewardUpdateTimestamps(block.timestamp, operatorRewardUpdateTimestamp);
      emit CommunityPoolRewardUpdated(s_rewardBuckets.communityBase.vestedRewardPerToken);
    } else if (stakerType == StakerType.OPERATOR) {
      if (operatorRewardUpdateTimestamp == block.timestamp) {
        return;
      }

      uint256 operatorTotalPrincipal = _getTotalPrincipal(i_operatorStakingPool);
      s_rewardBuckets.operatorBase.vestedRewardPerToken = _calculateVestedRewardPerToken(
        s_rewardBuckets.operatorBase, operatorTotalPrincipal, operatorRewardUpdateTimestamp
      ).toUint80();
      s_rewardBuckets.operatorDelegated.vestedRewardPerToken = _calculateVestedRewardPerToken(
        s_rewardBuckets.operatorDelegated, operatorTotalPrincipal, operatorRewardUpdateTimestamp
      ).toUint80();

      _setRewardUpdateTimestamps(communityRewardUpdateTimestamp, block.timestamp);
      emit OperatorPoolRewardUpdated(
        s_rewardBuckets.operatorBase.vestedRewardPerToken,
        s_rewardBuckets.operatorDelegated.vestedRewardPerToken
      );
    }
  }

  /// @notice Util function for calculating the current reward per token for the pools
  /// @return uint256 The community reward per token
  /// @return uint256 The operator reward per token
  /// @return uint256 The operator delegated reward per token
  function _calculatePoolsRewardPerToken() private view returns (uint256, uint256, uint256) {
    uint256 communityTotalPrincipal = _getTotalPrincipal(i_communityStakingPool);
    uint256 operatorTotalPrincipal = _getTotalPrincipal(i_operatorStakingPool);
    (uint256 communityRewardUpdateTimestamp, uint256 operatorRewardUpdateTimestamp) =
      _getRewardUpdateTimestamps(s_packedRewardUpdateTimestamps);

    return (
      _calculateVestedRewardPerToken(
        s_rewardBuckets.communityBase, communityTotalPrincipal, communityRewardUpdateTimestamp
        ),
      _calculateVestedRewardPerToken(
        s_rewardBuckets.operatorBase, operatorTotalPrincipal, operatorRewardUpdateTimestamp
        ),
      _calculateVestedRewardPerToken(
        s_rewardBuckets.operatorDelegated, operatorTotalPrincipal, operatorRewardUpdateTimestamp
        )
    );
  }

  /// @notice Calculate a bucket’s available rewards earned per token
  /// @param rewardBucket The reward bucket to calculate the vestedRewardPerToken for
  /// @param totalPrincipal The total staked LINK amount staked in a pool associated with the reward
  /// bucket
  /// @return uint256 The available rewards earned per token
  function _calculateVestedRewardPerToken(
    RewardBucket memory rewardBucket,
    uint256 totalPrincipal,
    uint256 lastUpdateTimestamp
  ) private view returns (uint256) {
    if (totalPrincipal == 0) return rewardBucket.vestedRewardPerToken;

    uint256 latestRewardEmittedAt = Math.min(rewardBucket.rewardDurationEndsAt, block.timestamp);

    if (latestRewardEmittedAt <= lastUpdateTimestamp) {
      return rewardBucket.vestedRewardPerToken;
    }

    uint256 elapsedTime = latestRewardEmittedAt - lastUpdateTimestamp;

    return rewardBucket.vestedRewardPerToken
      + (elapsedTime * rewardBucket.emissionRate).divWadDown(totalPrincipal);
  }

  /// @notice Calculates a stakers earned base reward
  /// @param stakerReward The staker's reward info
  /// @param stakerPrincipal The staker's staked LINK amount
  /// @param baseRewardPerToken The base reward per token of the staking pool
  /// @return uint256 The earned base reward
  function _calculateEarnedBaseReward(
    StakerReward memory stakerReward,
    uint256 stakerPrincipal,
    uint256 baseRewardPerToken
  ) private pure returns (uint256) {
    uint256 earnedBaseReward = _calculateAccruedReward({
      principal: stakerPrincipal,
      rewardPerToken: stakerReward.baseRewardPerToken,
      vestedRewardPerToken: baseRewardPerToken
    });

    return earnedBaseReward;
  }

  /// @notice Calculates an operator's earned delegated reward
  /// @param stakerReward The staker's reward info
  /// @param stakerPrincipal The staker's staked LINK amount
  /// @param operatorDelegatedRewardPerToken The operator delegated reward per token
  /// @return uint256 The earned delegated reward
  function _calculateEarnedDelegatedReward(
    StakerReward memory stakerReward,
    uint256 stakerPrincipal,
    uint256 operatorDelegatedRewardPerToken
  ) private pure returns (uint256) {
    uint256 earnedDelegatedReward = _calculateAccruedReward({
      principal: stakerPrincipal,
      rewardPerToken: stakerReward.operatorDelegatedRewardPerToken,
      vestedRewardPerToken: operatorDelegatedRewardPerToken
    });

    return earnedDelegatedReward;
  }

  /// @notice Calculates the newly accrued reward of a staker since the last time the staker's
  /// reward was updated
  /// @param principal The staker's staked LINK amount
  /// @param rewardPerToken The base or delegated reward per token of the staker
  /// @param vestedRewardPerToken The available reward per token of the staking pool
  /// @return uint256 The accrued reward amount
  function _calculateAccruedReward(
    uint256 principal,
    uint256 rewardPerToken,
    uint256 vestedRewardPerToken
  ) private pure returns (uint256) {
    return principal.mulWadDown(vestedRewardPerToken - rewardPerToken);
  }

  /// @notice Calculates and updates a staker's rewards
  /// @param staker The staker's address
  /// @param isOperator True if the staker is an operator, false otherwise
  /// @param stakerPrincipal The staker's staked LINK amount
  /// @dev Staker rewards are forfeited when a staker unstakes before they
  /// have reached their maximum ramp up period multiplier.  Additionally an
  /// operator will also forfeit any unclaimed rewards if they are removed
  /// before they reach the maximum ramp up period multiplier.
  /// @return StakerReward The staker's updated reward info
  function _calculateStakerReward(
    address staker,
    bool isOperator,
    uint256 stakerPrincipal
  ) private view returns (StakerReward memory) {
    StakerReward memory stakerReward = s_rewards[staker];

    if (stakerReward.stakerType != StakerType.NOT_STAKED) {
      // do nothing
    } else {
      stakerReward.stakerType = isOperator ? StakerType.OPERATOR : StakerType.COMMUNITY;
    }

    // Calculate earned base rewards
    stakerReward.unvestedBaseReward += _calculateEarnedBaseReward({
      stakerReward: stakerReward,
      stakerPrincipal: stakerPrincipal,
      baseRewardPerToken: isOperator
        ? s_rewardBuckets.operatorBase.vestedRewardPerToken
        : s_rewardBuckets.communityBase.vestedRewardPerToken
    }).toUint112();

    // Calculate earned delegated rewards if the staker is an operator
    if (isOperator) {
      // Multipliers do not apply to the delegation reward, i.e. always treat them as
      // multiplied by the max multiplier, which is 1.
      stakerReward.vestedDelegatedReward += _calculateEarnedDelegatedReward({
        stakerReward: stakerReward,
        stakerPrincipal: stakerPrincipal,
        operatorDelegatedRewardPerToken: s_rewardBuckets.operatorDelegated.vestedRewardPerToken
      }).toUint112();
    }

    // Update the staker's earned reward per token
    _updateStakerRewardPerToken(stakerReward, isOperator);

    return stakerReward;
  }

  /// @notice Helper function for calculating the available reward per token and the reclaimable
  /// reward
  /// @dev If the pool the staker is in is empty and we can't calculate the reward per token, we
  /// allow the staker to reclaim the forfeited reward.
  /// @param forfeitedReward The amount of forfeited reward
  /// @param amountOfRecipientTokens The amount of tokens that the forfeited rewards should be
  /// shared to
  /// @return uint256 The amount of shared forfeited reward
  /// @return uint256 The shared forfeited reward per token
  /// @return uint256 The amount of reclaimable reward
  function _calculateForfeitedRewardDistribution(
    uint256 forfeitedReward,
    uint256 amountOfRecipientTokens
  ) private pure returns (uint256, uint256, uint256) {
    if (forfeitedReward == 0) return (0, 0, 0);

    uint256 vestedReward;
    uint256 vestedRewardPerToken;
    uint256 reclaimableReward;

    if (amountOfRecipientTokens != 0) {
      vestedReward = forfeitedReward;
      vestedRewardPerToken = forfeitedReward.divWadDown(amountOfRecipientTokens);
    } else {
      reclaimableReward = forfeitedReward;
    }

    return (vestedReward, vestedRewardPerToken, reclaimableReward);
  }

  /// @notice Updates the staker's base and/or delegated reward per token values
  /// @dev This function is called when staking, unstaking, claiming rewards, finalizing rewards for
  /// removed operators, and slashing operators.
  /// @param stakerReward The staker reward struct
  /// @param isOperator Whether the staker is an operator or not
  function _updateStakerRewardPerToken(
    StakerReward memory stakerReward,
    bool isOperator
  ) private view {
    if (isOperator) {
      stakerReward.baseRewardPerToken = s_rewardBuckets.operatorBase.vestedRewardPerToken;
      stakerReward.operatorDelegatedRewardPerToken =
        s_rewardBuckets.operatorDelegated.vestedRewardPerToken;
    } else {
      stakerReward.baseRewardPerToken = s_rewardBuckets.communityBase.vestedRewardPerToken;
    }
  }

  /// @notice Calculates a staker's earned rewards
  /// @param staker The staker
  /// @return The staker reward info
  /// @return The forfeited reward
  function _getReward(
    address staker,
    uint256 stakerPrincipal,
    bool isOperator
  ) private view returns (StakerReward memory, uint256) {
    StakerReward memory stakerReward = s_rewards[staker];

    // Calculate latest reward per token for the pools
    (
      uint256 communityRewardPerToken,
      uint256 operatorRewardPerToken,
      uint256 operatorDelegatedRewardPerToken
    ) = _calculatePoolsRewardPerToken();

    // Calculate earned base rewards
    stakerReward.unvestedBaseReward += _calculateEarnedBaseReward({
      stakerReward: stakerReward,
      stakerPrincipal: stakerPrincipal,
      baseRewardPerToken: isOperator ? operatorRewardPerToken : communityRewardPerToken
    }).toUint112();

    // If operator Calculate earned delegated rewards
    if (isOperator) {
      // Multipliers do not apply to the delegation reward, i.e. always treat them as
      // multiplied by the max multiplier, which is 1.
      stakerReward.vestedDelegatedReward += _calculateEarnedDelegatedReward({
        stakerReward: stakerReward,
        stakerPrincipal: stakerPrincipal,
        operatorDelegatedRewardPerToken: operatorDelegatedRewardPerToken
      }).toUint112();
    }

    uint112 newVestedBaseRewards = _calculateNewVestedBaseRewards({
      stakerReward: stakerReward,
      multiplier: _getMultiplier(
        _getStakerStakedAtTime(
          staker,
          isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool)
        )
        )
    });

    stakerReward.vestedBaseReward += newVestedBaseRewards;
    uint256 forfeitedRewards = stakerReward.unvestedBaseReward - newVestedBaseRewards;

    // Forfeit rewards
    delete stakerReward.unvestedBaseReward;

    return (stakerReward, forfeitedRewards);
  }

  /// @notice Calculates the amount of unvested rewards in a reward bucket
  /// @param bucket The bucket to calculate unvested rewards for
  /// @return uint256 The amount of unvested rewards in the bucket
  function _getUnvestedRewards(RewardBucket memory bucket) private view returns (uint256) {
    return bucket.rewardDurationEndsAt <= block.timestamp
      ? 0
      : bucket.emissionRate * (bucket.rewardDurationEndsAt - block.timestamp);
  }

  /// @notice Returns whether or not an address is currently an operator or
  /// is a removed operator
  /// @param staker The staker address
  /// @return bool True if the staker is either an operator or a removed operator.
  function _isOperator(address staker) private view returns (bool) {
    return i_operatorStakingPool.isOperator(staker) || i_operatorStakingPool.isRemoved(staker);
  }

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

  /// @dev Reverts if the msg.sender doesn't have the rewarder role.
  modifier onlyRewarder() {
    if (!hasRole(REWARDER_ROLE, msg.sender)) {
      revert AccessForbidden();
    }
    _;
  }

  /// @dev Reverts if the msg.sender is not a valid staking pool
  modifier onlyStakingPool() {
    if (
      msg.sender != address(i_operatorStakingPool) && msg.sender != address(i_communityStakingPool)
    ) {
      revert AccessForbidden();
    }
    _;
  }

  /// @dev Reverts if the reward vault has been closed
  modifier whenOpen() {
    if (!s_vaultConfig.isOpen) revert VaultAlreadyClosed();
    _;
  }

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

  /// @inheritdoc TypeAndVersionInterface
  function typeAndVersion() external pure virtual override returns (string memory) {
    return "RewardVault 1.0.0";
  }
}

File 3 of 34 : 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 4 of 34 : TypeAndVersionInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

File 5 of 34 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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 == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 6 of 34 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

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

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

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

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

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

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

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

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

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

File 7 of 34 : FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

File 8 of 34 : IRewardVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IRewardVault {
  /// @notice This enum describes the different staker types
  enum StakerType {
    NOT_STAKED,
    COMMUNITY,
    OPERATOR
  }

  /// @notice This struct is used to store the reward information for a staker.
  struct StakerReward {
    /// @notice The staker's accrued multiplier-applied reward that's accounted for and stored.
    /// This is used for storing delegated rewards and preserving the staker's past rewards between
    /// unstakes or multiplier resets.
    /// To get the full claimable reward amount, this value is added to the stored reward *
    /// multiplier.
    /// @dev This value is reset when a staker calls claimRewards and rewards
    /// are transferred to the staker.
    uint112 vestedBaseReward;
    /// @notice The staker's accrued delegated reward that's accounted for and stored.
    /// Delegated rewards are not subject to the ramp up multiplier and are immediately finalized.
    /// @dev This value is reset when a staker calls claimRewards and rewards
    /// are transferred to the staker.
    uint112 vestedDelegatedReward;
    /// @notice The last updated per-token base reward of the staker.  This
    /// value only increases over time
    uint112 baseRewardPerToken;
    /// @notice The last updated per-token delegated reward of the operator
    uint112 operatorDelegatedRewardPerToken;
    /// @notice The staker type
    /// @dev This value is set once the first time a staker stakes. This value is used to enforce
    /// that a community staker is not added as an operator.
    StakerType stakerType;
    /// @notice The amount of base rewards that the staker has claimed between
    /// the last time they staked/unstaked until they stake, unstake again or
    /// when an operator is removed.
    /// @dev This is reset to 0 whenever concludeRewardPeriod is called
    /// @dev This is set to vestedBaseReward whenever claimReward is called
    /// @dev Invariant: The sum of unvestedBaseReward and claimedBaseRewardsInPeriod
    /// is the total amount of base rewards a staker has earned since the last time
    /// they stake/unstake.
    uint112 claimedBaseRewardsInPeriod;
    /// @notice The staker's earned but unvested base rewards. The staker's current multiplier is
    /// applied to get the vested base reward amount.
    uint112 unvestedBaseReward;
  }

  /// @notice Claims reward earned by a staker.
  /// @return uint256 The amount of rewards claimed in juels
  function claimReward() external returns (uint256);

  /// @notice Updates the staking pools' reward per token and staker’s reward state
  /// in the reward vault. This is called whenever an operator is slashed as we want
  /// to update the operator's rewards state without resetting their multiplier.
  /// @param staker The staker's address. If this is set to zero address,
  /// staker's reward update will be skipped
  /// @param stakerPrincipal The staker's current staked LINK amount in juels
  function updateReward(address staker, uint256 stakerPrincipal) external;

  /// @notice Concludes the staker's current reward period (defined by a multiplier reset).
  /// This will apply the staker's current ramp up multiplier to their
  /// earned rewards and store the amount of rewards they have earned before
  /// their multiplier is reset.
  /// @dev This is called whenever 1) A staker stakes 2) A staker unstakes
  /// 3) An operator is removed as we want to update the staker's
  /// rewards AND reset their multiplier.
  /// @dev Staker rewards are not forfeited when they stake before they have
  /// reached their maximum ramp up period multiplier.  Instead these
  /// rewards are stored as already earned rewards and will be subject to the
  /// multiplier the next time the contract calculates the staker's claimable
  /// rewards.
  /// @dev Staker rewards are forfeited when a staker unstakes before they
  /// have reached their maximum ramp up period multiplier.  Additionally an
  /// operator will also forfeit any unclaimable rewards if they are removed
  /// before they reach the maximum ramp up period multiplier.  The amount of
  /// rewards forfeited is proportional to the amount unstaked relative to
  /// the staker's total staked LINK amount when unstaking.  A removed operator forfeits
  /// 100% of their unclaimable rewards.
  /// @param staker The staker addres
  /// @param oldPrincipal The staker's staked LINK amount before finalizing
  /// @param stakedAt The last time the staker staked at
  /// @param unstakedAmount The amount that the staker has unstaked in juels
  /// @param shouldForfeit True if rewards should be forfeited
  function concludeRewardPeriod(
    address staker,
    uint256 oldPrincipal,
    uint256 stakedAt,
    uint256 unstakedAmount,
    bool shouldForfeit
  ) external;

  /// @notice Closes the reward vault, disabling adding rewards and staking
  function close() external;

  /// @notice Returns a boolean that is true if the reward vault is open
  /// @return True if open, false otherwise
  function isOpen() external view returns (bool);

  /// @notice Returns the rewards that the staker would get if they withdraw now
  /// Rewards calculation is based on the staker's multiplier
  /// @param staker The staker's address
  /// @return The reward amount
  function getReward(address staker) external view returns (uint256);

  /// @notice Returns the stored reward info of the staker
  /// @param staker The staker address
  /// @return The staker's stored reward info
  function getStoredReward(address staker) external view returns (StakerReward memory);

  /// @notice Returns whether or not the vault is paused
  /// @return bool True if the vault is paused
  function isPaused() external view returns (bool);

  /// @notice Returns whether or not the reward duration for the pool has ended
  /// @param stakingPool The address of the staking pool rewards are being shared to
  /// @return bool True if the reward duration has ended
  function hasRewardDurationEnded(address stakingPool) external view returns (bool);

  /// @notice Returns whether or not the reward vault has had rewards added to it
  /// @return bool True if the reward vault has had rewards added to it
  function hasRewardAdded() external view returns (bool);

  /// @notice Returns the staking pools that are earning rewards from
  /// the reward vault
  /// @return address[] The staking pools that are earning rewards from the
  /// reward vault
  function getStakingPools() external view returns (address[] memory);
}

File 9 of 34 : IStakingPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {IRewardVault} from "./IRewardVault.sol";
import {Checkpoints} from "@openzeppelin/contracts/utils/Checkpoints.sol";

interface IStakingPool {
  /// @notice This error is thrown when a caller tries to execute a transaction
  /// that they do not have permissions for
  error AccessForbidden();

  /// @notice This event is emitted when the migration proxy address has been set
  /// @param oldMigrationProxy The old migration proxy contract address
  /// @param newMigrationProxy The new migration proxy contract address
  event MigrationProxySet(address indexed oldMigrationProxy, address indexed newMigrationProxy);

  /// @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 the stakers in the pool is increased
  /// @param maxPrincipalPerStaker the new maximum stake amount
  event MaxPrincipalAmountIncreased(uint256 maxPrincipalPerStaker);

  /// @notice This event is emitted when a staker adds stake to the pool.
  /// @param staker Staker address
  /// @param amount Amount of stake added
  /// @param newStake New LINK amount staked
  /// @param newTotalPrincipal Total amount of juels staked in the pool
  event Staked(address indexed staker, uint256 amount, uint256 newStake, uint256 newTotalPrincipal);

  /// @notice This event is emitted when a staker removes stake from the pool.
  /// @param staker Staker address
  /// @param amount Amount of stake removed
  /// @param newStake New LINK amount staked
  /// @param newTotalPrincipal Total amount of staked juels remaining in the pool
  event Unstaked(
    address indexed staker, uint256 amount, uint256 newStake, uint256 newTotalPrincipal
  );

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

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

  /// @notice This error is thrown whenever the migration proxy address has not been set
  error MigrationProxyNotSet();

  /// @notice This error is thrown whenever the reward vault address has not been set
  error RewardVaultNotSet();

  /// @notice This error is thrown when invalid data is passed to the onTokenTransfer function
  error InvalidData();

  /// @notice This error is thrown when the staker tries to stake less than the min amount
  error InsufficientStakeAmount();

  /// @notice This error is thrown when the staker tries to stake more than the max amount
  error ExceedsMaxStakeAmount();

  /// @notice This error is thrown when the staker tries to stake more than the max pool size
  error ExceedsMaxPoolSize();

  /// @notice This error is raised when stakers attempt to exit the pool
  /// @param staker address of the staker
  error StakeNotFound(address staker);

  /// @notice This error is thrown when the staker tries to unstake a zero amount
  error UnstakeZeroAmount();

  /// @notice This error is thrown when the staker tries to unstake more than the
  /// staked LINK amount
  error UnstakeExceedsPrincipal();

  /// @notice This error is thrown when the staker tries to unstake an amount that leaves their
  /// staked LINK amount below the minimum amount
  error UnstakePrincipalBelowMinAmount();

  /// @notice This struct defines the state of a staker
  struct Staker {
    /// @notice The combined staked LINK amount and staked at time history
    /// @dev Both the staker staked LINK amount and staked at timestamp are stored in uint112 to
    /// save space
    /// @dev The max value of uint112 is greater than the total supply of LINK
    /// @dev The max value of uint112 can represent a timestamp in the year 3615, long after the
    /// staking program has ended
    /// @dev The combination is performed as such:
    /// uint224 history = (uint224(uint112(principal)) << 112) |
    /// uint224(uint112(stakedAtTime))
    Checkpoints.History history;
    /// @notice The staker's unbonding period end time
    uint128 unbondingPeriodEndsAt;
    /// @notice The staker's claim period end time
    uint128 claimPeriodEndsAt;
  }

  /// @notice Unstakes amount LINK tokens from the staker’s staked LINK amount
  /// @param amount The amount of LINK tokens to unstake
  function unstake(uint256 amount) external;

  /// @notice Returns the total amount staked in the pool
  /// @return The total amount staked in pool
  function getTotalPrincipal() external view returns (uint256);

  /// @notice Returns the staker's staked LINK amount
  /// @param staker The address of the staker to query for
  /// @return uint256 The staker's staked LINK amount
  function getStakerPrincipal(address staker) external view returns (uint256);

  /// @notice Returns the staker's staked LINK amount
  /// @param staker The address of the staker to query for
  /// @param blockNumber The block number to fetch the staker's balance for.  Pass 0
  /// to return the staker's latest staked LINK amount
  /// @return uint256 The staker's staked LINK amount
  function getStakerPrincipalAt(
    address staker,
    uint256 blockNumber
  ) external view returns (uint256);

  /// @notice Returns the staker's average staked at time
  /// @param staker The address of the staker to query for
  /// @return uint256 The staker's average staked at time
  function getStakerStakedAtTime(address staker) external view returns (uint256);

  /// @notice Returns the staker's last staked at time for a block number ID
  /// @param staker The address of the staker to query for
  /// @param blockNumber The block number to query for
  /// @return uint256 The staker's staked at time for the block number ID
  function getStakerStakedAtTimeAt(
    address staker,
    uint256 blockNumber
  ) external view returns (uint256);

  /// @notice Returns the current reward vault address
  /// @return The reward vault
  function getRewardVault() external view returns (IRewardVault);

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

  /// @notice Returns the migration proxy contract address
  /// @return The migration proxy contract address
  function getMigrationProxy() external view returns (address);

  /// @notice Returns a boolean that is true if the pool is open
  /// @return True if the pool is open, false otherwise
  function isOpen() external view returns (bool);

  /// @notice Returns a boolean that is true if the pool is active,
  /// i.e. is open and there are remaining rewards to vest in the pool.
  /// @return True if the pool is active, false otherwise
  function isActive() external view returns (bool);

  /// @notice Returns the minimum and maximum amounts a staker can stake in the
  /// pool
  /// @return uint256 minimum amount that can be staked by a staker
  /// @return uint256 maximum amount that can be staked by a staker
  function getStakerLimits() external view returns (uint256, uint256);

  /// @notice uint256 Returns the maximum amount that can be staked in the pool
  /// @return uint256 current maximum staking pool size
  function getMaxPoolSize() external view returns (uint256);
}

File 10 of 34 : PausableWithAccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {AccessControlDefaultAdminRules} from
  "@openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";

import {IPausable} from "./interfaces/IPausable.sol";

/// @notice Base contract that adds pausing and access control functionality.
abstract contract PausableWithAccessControl is IPausable, Pausable, AccessControlDefaultAdminRules {
  /// @notice This is the ID for the pauser role, which is given to the addresses that can pause and
  /// unpause the contract.
  /// @dev Hash: 65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

  constructor(
    uint48 adminRoleTransferDelay,
    address defaultAdmin
  ) AccessControlDefaultAdminRules(adminRoleTransferDelay, defaultAdmin) {}

  /// @inheritdoc IPausable
  function emergencyPause() external onlyRole(PAUSER_ROLE) {
    _pause();
  }

  /// @inheritdoc IPausable
  function emergencyUnpause() external onlyRole(PAUSER_ROLE) {
    _unpause();
  }
}

File 11 of 34 : CommunityStakingPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {TypeAndVersionInterface} from
  "@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol";

import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

import {IMerkleAccessController} from "../interfaces/IMerkleAccessController.sol";
import {OperatorStakingPool} from "./OperatorStakingPool.sol";
import {StakingPoolBase} from "./StakingPoolBase.sol";

/// @notice This contract manages the staking of LINK tokens for the community stakers.
/// @dev This contract inherits the StakingPoolBase contract and interacts with the MigrationProxy,
/// OperatorStakingPool, and RewardVault contracts.
/// @dev invariant Operators cannot stake in the community staking pool.
contract CommunityStakingPool is StakingPoolBase, IMerkleAccessController, TypeAndVersionInterface {
  /// @notice This error is thrown when the pool is opened with an empty
  /// merkle root
  error MerkleRootNotSet();

  /// @notice This event is emitted when the operator staking pool is changed
  /// @param oldOperatorStakingPool The old operator staking pool
  /// @param newOperatorStakingPool The new operator staking pool
  event OperatorStakingPoolChanged(
    address indexed oldOperatorStakingPool, address indexed newOperatorStakingPool
  );

  /// @notice This struct defines the params required by the Staking contract's
  /// constructor.
  struct ConstructorParams {
    /// @notice The base staking pool constructor parameters
    ConstructorParamsBase baseParams;
    /// @notice The operator staking pool contract
    OperatorStakingPool operatorStakingPool;
  }

  /// @notice The operator staking pool contract
  OperatorStakingPool private s_operatorStakingPool;
  /// @notice The merkle root of the merkle tree generated from the list
  /// of staker addresses with early access.
  bytes32 private s_merkleRoot;

  constructor(ConstructorParams memory params) StakingPoolBase(params.baseParams) {
    if (address(params.operatorStakingPool) == address(0)) {
      revert InvalidZeroAddress();
    }

    s_operatorStakingPool = params.operatorStakingPool;
  }

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

  /// @inheritdoc IMerkleAccessController
  function hasAccess(address staker, bytes32[] calldata proof) external view returns (bool) {
    return _hasAccess(staker, proof);
  }

  /// @inheritdoc IMerkleAccessController
  /// @dev precondition The caller must have the initiator admin role.
  /// @dev precondition Cannot be called after the pool is closed.
  function setMerkleRoot(bytes32 newMerkleRoot) external onlyRole(INITIATOR_ROLE) whenBeforeClosing {
    bytes32 oldMerkleRoot = s_merkleRoot;
    if (oldMerkleRoot == newMerkleRoot) return;

    s_merkleRoot = newMerkleRoot;
    emit MerkleRootChanged(oldMerkleRoot, newMerkleRoot);
  }

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

  /// @notice This function sets the operator staking pool
  /// @param newOperatorStakingPool The new operator staking pool
  /// @dev precondition The caller must have the default admin role.
  function setOperatorStakingPool(OperatorStakingPool newOperatorStakingPool)
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
  {
    if (address(newOperatorStakingPool) == address(0)) revert InvalidZeroAddress();
    address oldOperatorStakingPool = address(s_operatorStakingPool);
    if (oldOperatorStakingPool == address(newOperatorStakingPool)) return;

    s_operatorStakingPool = newOperatorStakingPool;
    emit OperatorStakingPoolChanged(oldOperatorStakingPool, address(newOperatorStakingPool));
  }

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

  /// @inheritdoc TypeAndVersionInterface
  function typeAndVersion() external pure virtual override returns (string memory) {
    return "CommunityStakingPool 1.0.0";
  }

  // ===============
  // StakingPoolBase
  // ===============

  /// @inheritdoc StakingPoolBase
  function _validateOnTokenTransfer(
    address sender,
    address staker,
    bytes calldata data
  ) internal view override(StakingPoolBase) {
    // check if staker has access
    // if the sender is the migration proxy, the staker is allowed to stake
    // if currently in public phase (merkle root set to empty bytes) data is ignored
    // if in the access limited phase data is the merkle proof
    // if in migrations only phase, the merkle root is set to double hash of the migration proxy
    // address. This is essentially only used as a placeholder to differentiate between the open
    // phase (empty merkle root) and access limited phase (merkle root generated from allowlist)
    if (
      sender != address(s_migrationProxy) && s_merkleRoot != bytes32(0)
        && !_hasAccess(staker, abi.decode(data, (bytes32[])))
    ) {
      revert AccessForbidden();
    }

    // check if the sender is an operator
    if (s_operatorStakingPool.isOperator(staker) || s_operatorStakingPool.isRemoved(staker)) {
      revert AccessForbidden();
    }
  }

  /// @inheritdoc StakingPoolBase
  function _validateBeforeOpen() internal view override(StakingPoolBase) {
    if (s_merkleRoot == bytes32(0)) {
      revert MerkleRootNotSet();
    }
  }

  /// @notice Util function that validates if a community staker has access to an
  /// access limited community staking pool
  /// @param staker The community staker's address
  /// @param proof Merkle proof for the community staker's allowlist
  /// @return bool True if the community staker has access to the access limited
  /// community staking pool
  function _hasAccess(address staker, bytes32[] memory proof) private view returns (bool) {
    if (s_merkleRoot == bytes32(0)) return true;
    return MerkleProof.verify({
      proof: proof,
      root: s_merkleRoot,
      leaf: keccak256(bytes.concat(keccak256(abi.encode(staker))))
    });
  }
}

File 12 of 34 : OperatorStakingPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {TypeAndVersionInterface} from
  "@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol";

import {AccessControlDefaultAdminRules} from
  "@openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol";
import {Checkpoints} from "@openzeppelin/contracts/utils/Checkpoints.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

import {ISlashable} from "../interfaces/ISlashable.sol";
import {IRewardVault} from "../interfaces/IRewardVault.sol";
import {StakingPoolBase} from "./StakingPoolBase.sol";

/// @notice This contract manages the staking of LINK tokens for the operator stakers.
/// @dev This contract inherits the StakingPoolBase contract and interacts with the MigrationProxy,
/// PriceFeedAlertsController, CommunityStakingPool, and RewardVault contracts.
/// @dev invariant Only addresses added as operators by the contract manager can stake in this pool.
/// @dev invariant contract's LINK token balance should be greater than or equal to the sum of
/// totalPrincipal and s_alerterRewardFunds.
contract OperatorStakingPool is ISlashable, StakingPoolBase, TypeAndVersionInterface {
  using Checkpoints for Checkpoints.History;
  using EnumerableSet for EnumerableSet.AddressSet;

  /// @notice This error is raised when adding the zero address as an operator
  error InvalidOperator();
  /// @notice Error code for when the operator list is invalid
  error InvalidOperatorList();
  /// @notice Error code for when the staker is not an operator
  error StakerNotOperator();
  /// @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 raised when removing an operator that doesn't exist.
  /// @param operator Address of the operator
  error OperatorDoesNotExist(address operator);
  /// @notice This error is raised when an operator to add has been removed previously.
  /// @param operator Address of the operator
  error OperatorHasBeenRemoved(address operator);
  /// @notice This error is raised when an operator to add is already a community staker.
  error OperatorCannotBeCommunityStaker(address operator);
  /// @notice This error is thrown whenever the max pool size is less than the
  /// reserved space for operators
  /// @param maxPoolSize The maximum pool size of the operator staking pool
  /// @param maxPrincipalPerStaker The maximum amount an operator can stake in the
  /// pool
  /// @param numOperators The number of operators in the pool
  error InsufficientPoolSpace(
    uint256 maxPoolSize, uint256 maxPrincipalPerStaker, uint256 numOperators
  );
  /// @notice This error is raised when attempting to open the staking pool with less
  /// than the minimum required node operators
  /// @param numOperators The current number of operators in the staking pool
  /// @param minInitialOperatorCount The minimum required number of operators
  /// in the staking pool before it can be opened
  error InadequateInitialOperatorCount(uint256 numOperators, uint256 minInitialOperatorCount);
  /// @notice This error is thrown when the contract manager tries to add a zero amount
  /// to the alerter reward funds
  error InvalidAlerterRewardFundAmount();
  /// @notice This error is thrown whenever the contract manager tries to withdraw
  /// more than the remaining balance in the alerter reward funds
  /// @param amountToWithdraw The amount that the contract manager tried to withdraw
  /// @param remainingBalance The remaining balance of the alerter reward funds
  error InsufficientAlerterRewardFunds(uint256 amountToWithdraw, uint256 remainingBalance);

  /// @notice This event is emitted when an operator is removed
  /// @param operator Address of the operator
  /// @param principal Operator's staked LINK amount
  /// @param newTotalPrincipal Total amount of staked juels remaining in the pool
  event OperatorRemoved(address indexed operator, uint256 principal, uint256 newTotalPrincipal);
  /// @notice This event is emitted when an operator is added
  /// @param operator Address of the operator
  event OperatorAdded(address indexed operator);
  /// @notice This event is emitted whenever the alerter reward funds is funded
  /// @param amountFunded The amount added to the alerter reward funds
  /// @param totalBalance  The current balance of the alerter reward funds
  event AlerterRewardDeposited(uint256 amountFunded, uint256 totalBalance);
  /// @notice This event is emitted whenever the contract manager withdraws from the
  /// alerter reward funds
  /// @param amountWithdrawn The amount withdrawn from the alerter reward funds
  /// @param remainingBalance The remaining balance of the alerter reward funds
  event AlerterRewardWithdrawn(uint256 amountWithdrawn, uint256 remainingBalance);
  /// @notice This event is emitted whenever the alerter is paid the full
  /// alerter reward amount
  /// @param alerter The address of the alerter
  /// @param alerterRewardActual The amount of rewards sent to the alerter in juels.
  /// This can be lower than the expected value, if the reward fund is low or we aren't able to
  /// slash enough.
  /// @param alerterRewardExpected The amount of expected rewards for the alerter
  /// in juels
  event AlertingRewardPaid(
    address indexed alerter, uint256 alerterRewardActual, uint256 alerterRewardExpected
  );
  /// @notice This event is emitted when the slasher config is set
  /// @param slasher The address of the slasher
  /// @param refillRate The refill rate of the slasher
  /// @param slashCapacity The slash capacity of the slasher
  event SlasherConfigSet(address indexed slasher, uint256 refillRate, uint256 slashCapacity);
  /// @notice This event is emitted when an operator is slashed
  /// @param operator The address of the operator
  /// @param slashedAmount The amount slashed from the operator's staked LINK
  /// amount
  /// @param updatedStakerPrincipal The operator's updated staked LINK amount
  /// @param newTotalPrincipal Total amount of staked juels remaining in the pool
  event Slashed(
    address indexed operator,
    uint256 slashedAmount,
    uint256 updatedStakerPrincipal,
    uint256 newTotalPrincipal
  );

  /// @notice This struct defines the params required by the Staking contract's
  /// constructor.
  struct ConstructorParams {
    /// @notice The base staking pool constructor parameters
    ConstructorParamsBase baseParams;
    /// @notice The minimum number of node operators required to open the
    /// staking pool.
    uint256 minInitialOperatorCount;
  }

  /// @notice This struct defines the operator-specific states.
  struct Operator {
    /// @notice The operator's staked LINK amount when they get removed.
    uint256 removedPrincipal;
    /// @notice Flag that signals whether the operator is an operator.
    bool isOperator;
    /// @notice Flag that signals whether the operator has been removed.
    bool isRemoved;
  }

  /// @notice This is the ID for the alert rewarder role, which is given to the
  /// addresses that will deposit and withdraw the alerter reward.
  /// @dev Hash: 8d2cf17e37ecc80f26d65bcf3868b78960ab38b0762747f6c5e311e75068a88b
  bytes32 public constant ALERT_REWARDER_ROLE = keccak256("ALERT_REWARDER_ROLE");
  /// @notice This is the ID for the operator manager role, which is given to the address that will
  /// add and remove operators
  /// @dev Hash: 001fdceeaab2d33566b504ecfe97e6dc3cf82cc816e696d9fe5cce35954bed17
  bytes32 public constant OPERATOR_MANAGER_ROLE = keccak256("OPERATOR_MANAGER_ROLE");
  /// @notice This is the ID for the slasher role, which will be given to the
  /// AlertsController contract.
  /// @dev Hash: 12b42e8a160f6064dc959c6f251e3af0750ad213dbecf573b4710d67d6c28e39
  bytes32 public constant SLASHER_ROLE = keccak256("SLASHER_ROLE");
  /// @notice Mapping of addresses to the Operator struct.
  mapping(address operator => Operator) private s_operators;
  /// @notice Mapping of the slashers to slasher config and state.
  mapping(address slasher => Slasher) private s_slashers;
  /// @notice The set of operators that are currently on-feed (slashable).
  EnumerableSet.AddressSet private s_operatorSet;
  /// @notice The number of node operators that have been set in the pool
  uint256 private s_numOperators;
  /// @notice Tracks the balance of the alerter reward funds.  This bucket holds all
  /// slashed funds and also funds alerter rewards.
  uint256 private s_alerterRewardFunds;
  /// @notice The minimum number of node operators required to open the
  /// staking pool.
  uint256 private immutable i_minInitialOperatorCount;

  constructor(ConstructorParams memory params) StakingPoolBase(params.baseParams) {
    i_minInitialOperatorCount = params.minInitialOperatorCount;
  }

  /// @notice Adds LINK to the alerter reward funds
  /// @param amount The amount of LINK to add to the alerter reward funds
  /// @dev precondition The caller must have the alert rewarder role.
  /// @dev precondition The caller must have at least `amount` LINK tokens.
  /// @dev precondition The caller must have approved this contract for the transfer of at least
  /// `amount` LINK tokens.
  function depositAlerterReward(uint256 amount)
    external
    onlyRole(ALERT_REWARDER_ROLE)
    whenBeforeClosing
  {
    if (amount == 0) revert InvalidAlerterRewardFundAmount();
    uint256 alerterRewardFunds = s_alerterRewardFunds;
    alerterRewardFunds += amount;
    s_alerterRewardFunds = alerterRewardFunds;
    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transferFrom({from: msg.sender, to: address(this), value: amount});
    emit AlerterRewardDeposited(amount, alerterRewardFunds);
  }

  /// @notice Withdraws LINK from the alerter reward funds
  /// @param amount The amount of LINK withdrawn from the alerter reward funds
  /// @dev precondition The caller must have the alert rewarder role.
  /// @dev precondition This contract must have at least `amount` LINK tokens as the alerter reward
  /// funds.
  /// @dev precondition This contract must be closed (before opening or after closing).
  function withdrawAlerterReward(uint256 amount) external onlyRole(ALERT_REWARDER_ROLE) {
    if (amount == 0) revert InvalidAlerterRewardFundAmount();
    if (s_isOpen) revert PoolNotClosed();
    uint256 alerterRewardFunds = s_alerterRewardFunds;
    if (amount > alerterRewardFunds) {
      revert InsufficientAlerterRewardFunds(amount, alerterRewardFunds);
    }
    alerterRewardFunds -= amount;
    s_alerterRewardFunds = alerterRewardFunds;
    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transfer(msg.sender, amount);
    emit AlerterRewardWithdrawn(amount, alerterRewardFunds);
  }

  /// @notice Returns the balance of the pool's alerter reward funds
  /// @return uint256 The balance of the pool's alerter reward funds
  function getAlerterRewardFunds() external view returns (uint256) {
    return s_alerterRewardFunds;
  }

  // ===============
  // StakingPoolBase
  // ===============

  /// @inheritdoc StakingPoolBase
  /// @dev The access control is done in StakingPoolBase.
  function setPoolConfig(
    uint256 maxPoolSize,
    uint256 maxPrincipalPerStaker
  )
    external
    override(StakingPoolBase)
    validatePoolSpace(maxPoolSize, maxPrincipalPerStaker, s_numOperators)
    whenOpen
    onlyRole(DEFAULT_ADMIN_ROLE)
  {
    _setPoolConfig(maxPoolSize, maxPrincipalPerStaker);
  }

  /// @inheritdoc StakingPoolBase
  /// @dev Removed operators need to go through the unbonding period before they can withdraw. This
  /// function will check if the operator has removed principal they can unstake.
  function unbond() external override {
    Staker storage staker = s_stakers[msg.sender];
    uint224 history = staker.history.latest();
    uint112 stakerPrincipal = uint112(history >> 112);
    if (stakerPrincipal == 0 && s_operators[msg.sender].removedPrincipal == 0) {
      revert StakeNotFound(msg.sender);
    }

    _unbond(staker);
  }

  /// @notice Registers operators from a list of unique, sorted addresses
  /// Addresses must be provided in sorted order so that
  /// address(0xNext) > address(0xPrev)
  /// @dev Previously removed operators cannot be readded to the pool.
  /// @dev precondition The caller must have the operator manager role.
  /// @dev precondition Cannot be called after the pool is closed.
  /// @param operators The sorted list of operator addresses
  function addOperators(address[] calldata operators)
    external
    whenBeforeClosing
    validateRewardVaultSet
    validatePoolSpace(
      s_pool.configs.maxPoolSize,
      s_pool.configs.maxPrincipalPerStaker,
      s_numOperators + operators.length
    )
    onlyRole(OPERATOR_MANAGER_ROLE)
  {
    for (uint256 i; i < operators.length; ++i) {
      address operatorAddress = operators[i];
      if (operatorAddress == address(0)) revert InvalidOperator();
      IRewardVault.StakerReward memory stakerReward = s_rewardVault.getStoredReward(operatorAddress);
      if (stakerReward.stakerType == IRewardVault.StakerType.COMMUNITY) {
        revert OperatorCannotBeCommunityStaker(operatorAddress);
      }
      // verify input list is sorted and addresses are unique
      if (i < operators.length - 1 && operatorAddress >= operators[i + 1]) {
        revert InvalidOperatorList();
      }
      Operator storage operator = s_operators[operatorAddress];
      if (operator.isOperator) revert OperatorAlreadyExists(operatorAddress);
      if (operator.isRemoved) revert OperatorHasBeenRemoved(operatorAddress);
      operator.isOperator = true;
      s_operatorSet.add(operatorAddress);
      emit OperatorAdded(operatorAddress);
    }

    unchecked {
      s_numOperators += operators.length;
    }
  }

  /// @notice Removes one or more operators from a list of operators.
  /// @dev Should only be callable by the owner when the pool is open.
  /// When an operator is removed, we store their staked LINK amount in a separate mapping to
  /// stop it from accruing rewards. They can withdraw their removedPrincipal and exit the system
  /// after going through the unbonding period.
  /// Removed operators are no longer slashable.
  /// @param operators A list of operator addresses to remove
  /// @dev precondition The caller must have the operator manager role.
  /// @dev precondition Cannot be called after the pool is closed.
  /// @dev precondition The operators must be currently added operators.
  function removeOperators(address[] calldata operators)
    external
    onlyRole(OPERATOR_MANAGER_ROLE)
    whenBeforeClosing
  {
    Operator storage operator;
    Staker storage staker;
    uint256 totalPrincipal = s_pool.state.totalPrincipal;
    for (uint256 i; i < operators.length; ++i) {
      address operatorAddress = operators[i];
      operator = s_operators[operatorAddress];
      if (!operator.isOperator) revert OperatorDoesNotExist(operatorAddress);

      staker = s_stakers[operatorAddress];
      uint224 history = staker.history.latest();
      uint256 principal = uint256(history >> 112);
      uint256 stakedAtTime = uint112(history);
      s_rewardVault.concludeRewardPeriod({
        staker: operatorAddress,
        oldPrincipal: principal,
        unstakedAmount: principal,
        shouldForfeit: true,
        stakedAt: stakedAtTime
      });

      totalPrincipal -= principal;
      s_pool.state.totalPrincipal = totalPrincipal;
      delete operator.isOperator;
      s_operatorSet.remove(operatorAddress);
      operator.isRemoved = true;
      // Reset the staker's stakedAtTime to 0 so their multiplier resets to 0.
      _updateStakerHistory({staker: staker, latestPrincipal: 0, latestStakedAtTime: 0});
      // Move the operator's staked LINK amount to removedPrincipal so that
      // the operator stops earning rewards
      operator.removedPrincipal = principal;

      _resetUnbondingPeriod(staker, operatorAddress);

      emit OperatorRemoved(operatorAddress, principal, totalPrincipal);
    }

    s_numOperators -= operators.length;
  }

  /// @notice Getter function to check if an address is registered as an operator
  /// @param staker The address of the staker
  /// @return bool True if the staker is an operator
  function isOperator(address staker) external view returns (bool) {
    return s_operators[staker].isOperator;
  }

  /// @notice Getter function to check if an address is a removed operator
  /// @param staker The address of the staker
  /// @return bool True if the operator has been removed
  function isRemoved(address staker) external view returns (bool) {
    return s_operators[staker].isRemoved;
  }

  /// @notice Getter function for a removed operator's total staked LINK amount
  /// @param staker The address of the staker
  /// @return uint256 The removed operator's staked LINK amount that hasn't been withdrawn
  function getRemovedPrincipal(address staker) external view returns (uint256) {
    return s_operators[staker].removedPrincipal;
  }

  /// @notice Called by removed operators to withdraw their removed stake
  /// @dev precondition The caller must be in the claim period or the pool must be closed or paused.
  /// @dev precondition The caller must be a removed operator with some removed
  /// staked LINK amount.
  function unstakeRemovedPrincipal() external {
    if (!_canUnstake(s_stakers[msg.sender])) {
      revert StakerNotInClaimPeriod(msg.sender);
    }

    uint256 withdrawableAmount = s_operators[msg.sender].removedPrincipal;
    if (withdrawableAmount == 0) {
      revert UnstakeExceedsPrincipal();
    }
    delete s_operators[msg.sender].removedPrincipal;

    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transfer(msg.sender, withdrawableAmount);
    // Since operator has been removed their total amount staked will be 0
    emit Unstaked(msg.sender, withdrawableAmount, 0, s_pool.state.totalPrincipal);
  }

  /// @notice Returns the number of operators configured in the pool.
  /// @return uint256 The number of operators configured in the pool
  function getNumOperators() external view returns (uint256) {
    return s_numOperators;
  }

  /// @notice Returns the list of operators configured in the pool.
  /// @return address[] The list of operators configured in the pool
  function getOperators() external view returns (address[] memory) {
    return s_operatorSet.values();
  }

  // =======================
  // ISlashable
  // =======================

  /// @inheritdoc ISlashable
  /// @dev precondition The caller must have the default admin role.
  /// @dev precondition Cannot be called after the pool is closed.
  function addSlasher(
    address slasher,
    SlasherConfig calldata config
  ) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing {
    _grantRole(SLASHER_ROLE, slasher);
    _setSlasherConfig(slasher, config);
  }

  /// @inheritdoc ISlashable
  /// @dev precondition The caller must have the default admin role.
  /// @dev precondition Cannot be called after the pool is closed.
  function removeSlasher(address slasher) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing {
    if (!hasRole(SLASHER_ROLE, slasher)) {
      revert InvalidSlasher();
    }
    delete s_slashers[slasher];
    _revokeRole(SLASHER_ROLE, slasher);
    emit SlasherConfigSet(slasher, 0, 0);
  }

  /// @inheritdoc ISlashable
  /// @dev precondition The caller must have the default admin role.
  /// @dev precondition Cannot be called after the pool is closed.
  function setSlasherConfig(
    address slasher,
    SlasherConfig calldata config
  ) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing {
    if (!hasRole(SLASHER_ROLE, slasher)) {
      revert InvalidSlasher();
    }
    _setSlasherConfig(slasher, config);
  }

  /// @inheritdoc ISlashable
  function getSlasherConfig(address slasher) external view returns (SlasherConfig memory) {
    return s_slashers[slasher].config;
  }

  /// @inheritdoc ISlashable
  function getSlashCapacity(address slasher) external view returns (uint256) {
    SlasherConfig memory slasherConfig = s_slashers[slasher].config;
    return _getRemainingSlashCapacity(slasherConfig, slasher);
  }

  /// @inheritdoc ISlashable
  /// @dev In the current implementation, on-feed operators can raise alerts to rescue a portion of
  /// their slashed staked LINK amount. All operators can raise alerts in the priority period. Note
  /// that this may change in the future as we add alerting for additional services.
  /// @dev We will operationally make sure to remove an operator from the slashable (on-feed)
  /// operators list in alerts controllers if they are removed from the operators list in this
  /// contract, so there won't be a case where we slash a removed operator.
  /// @dev precondition The caller must have the slasher role.
  /// @dev precondition This contract must be active (open and stakers are earning rewards).
  /// @dev precondition The slasher must have enough capacity to slash.
  function slashAndReward(
    address[] calldata stakers,
    address alerter,
    uint256 principalAmount,
    uint256 alerterRewardAmount
  ) external onlySlasher whenActive whenNotPaused {
    SlasherConfig storage slasherConfig = s_slashers[msg.sender].config;
    uint256 combinedSlashAmount = stakers.length * principalAmount;

    uint256 remainingSlashCapacity = _getRemainingSlashCapacity(slasherConfig, msg.sender);
    // check if the total slashed amount exceeds the slasher's capacity
    if (combinedSlashAmount > remainingSlashCapacity) {
      /// @dev If a slashing occurs with an amount to be slashed that is higher than the remaining
      /// slashing capacity, only an amount equal to the remaining capacity is slashed.
      principalAmount = remainingSlashCapacity / stakers.length;
    }

    uint256 totalSlashedAmount = _slashOperators(stakers, principalAmount);

    s_slashers[msg.sender].state.remainingSlashCapacityAmount =
      remainingSlashCapacity - totalSlashedAmount;
    s_slashers[msg.sender].state.lastSlashTimestamp = block.timestamp;

    _payAlerter({
      alerter: alerter,
      totalSlashedAmount: totalSlashedAmount,
      alerterRewardAmount: alerterRewardAmount
    });
  }

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

  /// @inheritdoc TypeAndVersionInterface
  function typeAndVersion() external pure virtual override returns (string memory) {
    return "OperatorStakingPool 1.0.0";
  }

  // ==============================
  // AccessControlDefaultAdminRules
  // ==============================

  /// @inheritdoc AccessControlDefaultAdminRules
  /// @notice Grants `role` to `account`. Reverts if the contract manager tries to grant the default
  /// admin or slasher role.
  /// @dev The default admin role must be granted through `beginDefaultAdminTransfer` and
  /// `acceptDefaultAdminTransfer`.
  /// @dev The slasher role must be granted through `addSlasher`.
  /// @param role The role to grant
  /// @param account The address to grant the role to
  function grantRole(
    bytes32 role,
    address account
  ) public virtual override(AccessControlDefaultAdminRules) {
    if (role == SLASHER_ROLE) revert InvalidRole();
    super.grantRole(role, account);
  }

  /// @inheritdoc AccessControlDefaultAdminRules
  /// @notice Grants `role` to `account`. Reverts if the contract manager tries to grant the default
  /// admin or slasher role.
  /// @dev The default admin role must be revoked through `beginDefaultAdminTransfer` and
  /// `acceptDefaultAdminTransfer` to another address.
  /// @dev The slasher role must be revoked through `removeSlasher`.
  /// @param role The role to revoke
  /// @param account The address to revoke the role from
  function revokeRole(
    bytes32 role,
    address account
  ) public virtual override(AccessControlDefaultAdminRules) {
    if (role == SLASHER_ROLE) revert InvalidRole();
    super.revokeRole(role, account);
  }

  // ===============
  // StakingPoolBase
  // ===============

  /// @inheritdoc StakingPoolBase
  function _validateOnTokenTransfer(
    address,
    address staker,
    bytes calldata
  ) internal view override(StakingPoolBase) {
    // check if staker is an operator
    if (!s_operators[staker].isOperator) revert StakerNotOperator();
  }

  /// @inheritdoc StakingPoolBase
  function _validateBeforeOpen() internal view override(StakingPoolBase) {
    if (s_numOperators < i_minInitialOperatorCount) {
      revert InadequateInitialOperatorCount(s_numOperators, i_minInitialOperatorCount);
    }
  }

  /// @notice Helper function to set the slasher config
  /// @param slasher The slasher
  /// @param config The slasher config
  function _setSlasherConfig(address slasher, SlasherConfig calldata config) private {
    if (config.slashCapacity == 0 || config.refillRate == 0) {
      revert ISlashable.InvalidSlasherConfig();
    }

    s_slashers[slasher].config = config;

    // refill capacity
    SlasherState storage state = s_slashers[slasher].state;
    state.remainingSlashCapacityAmount = config.slashCapacity;
    state.lastSlashTimestamp = block.timestamp;

    emit SlasherConfigSet(slasher, config.refillRate, config.slashCapacity);
  }

  /// @notice Helper function to slash operators
  /// @param operators The list of operators to slash
  /// @param principalAmount The amount to slash from each operator's staked
  /// LINK amount
  /// @return The total amount slashed from all operators
  function _slashOperators(
    address[] calldata operators,
    uint256 principalAmount
  ) private returns (uint256) {
    // perform the slash on all operators and add up the total slashed amount
    uint256 totalSlashedAmount;
    Staker storage staker;
    uint256 totalPrincipal = s_pool.state.totalPrincipal;
    for (uint256 i; i < operators.length; ++i) {
      // verify input list is sorted and addresses are unique
      address operatorAddress = operators[i];
      if (i < operators.length - 1 && operatorAddress >= operators[i + 1]) {
        revert InvalidOperatorList();
      }
      staker = s_stakers[operatorAddress];
      uint224 history = staker.history.latest();
      uint256 operatorPrincipal = uint112(history >> 112);
      uint256 stakerStakedAtTime = uint112(history);
      uint256 slashedAmount =
        principalAmount > operatorPrincipal ? operatorPrincipal : principalAmount;
      uint256 updatedPrincipal = operatorPrincipal - slashedAmount;

      // update the staker's rewards
      s_rewardVault.updateReward(operatorAddress, operatorPrincipal);
      _updateStakerHistory({
        staker: staker,
        latestPrincipal: updatedPrincipal,
        latestStakedAtTime: stakerStakedAtTime
      });

      totalSlashedAmount += slashedAmount;
      totalPrincipal -= slashedAmount;

      emit Slashed(operatorAddress, slashedAmount, updatedPrincipal, totalPrincipal);
    }
    // update the pool state
    s_pool.state.totalPrincipal = totalPrincipal;

    return totalSlashedAmount;
  }

  /// @notice Helper function to reward the alerter
  /// @param alerter The alerter
  /// @param totalSlashedAmount The total amount slashed from all the operators
  /// @param alerterRewardAmount The amount to reward the alerter
  function _payAlerter(
    address alerter,
    uint256 totalSlashedAmount,
    uint256 alerterRewardAmount
  ) private {
    uint256 newAlerterRewardFunds = s_alerterRewardFunds + totalSlashedAmount;
    uint256 alerterRewardActual =
      newAlerterRewardFunds < alerterRewardAmount ? newAlerterRewardFunds : alerterRewardAmount;
    s_alerterRewardFunds = newAlerterRewardFunds - alerterRewardActual;

    // We emit an event here instead of reverting so that the alerter can
    // immediately receive a portion of their rewards.  This event
    // will allow the contract manager to reimburse any remaining rewards to the
    // alerter.
    emit AlertingRewardPaid(alerter, alerterRewardActual, alerterRewardAmount);

    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transfer(alerter, alerterRewardActual);
  }

  /// @notice Helper function to return the current remaining slash capacity for a slasher
  /// @param slasherConfig The slasher's config
  /// @param slasher The slasher
  /// @return The remaining slashing capacity
  function _getRemainingSlashCapacity(
    SlasherConfig memory slasherConfig,
    address slasher
  ) private view returns (uint256) {
    SlasherState memory slasherState = s_slashers[slasher].state;
    uint256 refilledAmount =
      (block.timestamp - slasherState.lastSlashTimestamp) * slasherConfig.refillRate;

    return Math.min(
      slasherConfig.slashCapacity, slasherState.remainingSlashCapacityAmount + refilledAmount
    );
  }

  /// @dev Reverts if the msg.sender doesn't have the rewarder role.
  modifier onlyRewarder() {
    if (!hasRole(ALERT_REWARDER_ROLE, msg.sender)) {
      revert AccessForbidden();
    }
    _;
  }

  /// @dev Reverts if not sent by an address that has the SLASHER role
  modifier onlySlasher() {
    if (!hasRole(SLASHER_ROLE, msg.sender)) {
      revert AccessForbidden();
    }
    _;
  }

  /// @notice Checks that the maximum pool size is greater than or equal to
  /// the reserved space for operators.
  /// @param maxPoolSize The maximum pool size of the operator staking pool
  /// @param maxPrincipalPerStaker The maximum amount an operator can stake in the
  /// @param numOperators The number of operators in the pool
  /// @dev The reserved space is calculated by multiplying the number of
  /// operators and the maximum staked LINK amount per operator
  modifier validatePoolSpace(
    uint256 maxPoolSize,
    uint256 maxPrincipalPerStaker,
    uint256 numOperators
  ) {
    if (maxPoolSize < maxPrincipalPerStaker * numOperators) {
      revert InsufficientPoolSpace(maxPoolSize, maxPrincipalPerStaker, numOperators);
    }
    _;
  }
}

File 13 of 34 : Checkpoints.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Checkpoints.sol)
// This file was procedurally generated from scripts/generate/templates/Checkpoints.js.

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SafeCast.sol";

/**
 * @dev This library defines the `History` struct, for checkpointing values as they change at different points in
 * time, and later looking up past values by block number. See {Votes} as an example.
 *
 * To create a history of checkpoints define a variable type `Checkpoints.History` in your contract, and store a new
 * checkpoint for the current transaction block using the {push} function.
 *
 * _Available since v4.5._
 */
library Checkpoints {
    struct History {
        Checkpoint[] _checkpoints;
    }

    struct Checkpoint {
        uint32 _blockNumber;
        uint224 _value;
    }

    /**
     * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one
     * before it is returned, or zero otherwise. Because the number returned corresponds to that at the end of the
     * block, the requested block number must be in the past, excluding the current block.
     */
    function getAtBlock(History storage self, uint256 blockNumber) internal view returns (uint256) {
        require(blockNumber < block.number, "Checkpoints: block not yet mined");
        uint32 key = SafeCast.toUint32(blockNumber);

        uint256 len = self._checkpoints.length;
        uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one
     * before it is returned, or zero otherwise. Similar to {upperLookup} but optimized for the case when the searched
     * checkpoint is probably "recent", defined as being among the last sqrt(N) checkpoints where N is the number of
     * checkpoints.
     */
    function getAtProbablyRecentBlock(History storage self, uint256 blockNumber) internal view returns (uint256) {
        require(blockNumber < block.number, "Checkpoints: block not yet mined");
        uint32 key = SafeCast.toUint32(blockNumber);

        uint256 len = self._checkpoints.length;

        uint256 low = 0;
        uint256 high = len;

        if (len > 5) {
            uint256 mid = len - Math.sqrt(len);
            if (key < _unsafeAccess(self._checkpoints, mid)._blockNumber) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);

        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Pushes a value onto a History so that it is stored as the checkpoint for the current block.
     *
     * Returns previous value and new value.
     */
    function push(History storage self, uint256 value) internal returns (uint256, uint256) {
        return _insert(self._checkpoints, SafeCast.toUint32(block.number), SafeCast.toUint224(value));
    }

    /**
     * @dev Pushes a value onto a History, by updating the latest value using binary operation `op`. The new value will
     * be set to `op(latest, delta)`.
     *
     * Returns previous value and new value.
     */
    function push(
        History storage self,
        function(uint256, uint256) view returns (uint256) op,
        uint256 delta
    ) internal returns (uint256, uint256) {
        return push(self, op(latest(self), delta));
    }

    /**
     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
     */
    function latest(History storage self) internal view returns (uint224) {
        uint256 pos = self._checkpoints.length;
        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
     * in the most recent checkpoint.
     */
    function latestCheckpoint(
        History storage self
    ) internal view returns (bool exists, uint32 _blockNumber, uint224 _value) {
        uint256 pos = self._checkpoints.length;
        if (pos == 0) {
            return (false, 0, 0);
        } else {
            Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
            return (true, ckpt._blockNumber, ckpt._value);
        }
    }

    /**
     * @dev Returns the number of checkpoint.
     */
    function length(History storage self) internal view returns (uint256) {
        return self._checkpoints.length;
    }

    /**
     * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
     * or by updating the last one.
     */
    function _insert(Checkpoint[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) {
        uint256 pos = self.length;

        if (pos > 0) {
            // Copying to memory is important here.
            Checkpoint memory last = _unsafeAccess(self, pos - 1);

            // Checkpoint keys must be non-decreasing.
            require(last._blockNumber <= key, "Checkpoint: decreasing keys");

            // Update or push new checkpoint
            if (last._blockNumber == key) {
                _unsafeAccess(self, pos - 1)._value = value;
            } else {
                self.push(Checkpoint({_blockNumber: key, _value: value}));
            }
            return (last._value, value);
        } else {
            self.push(Checkpoint({_blockNumber: key, _value: value}));
            return (0, value);
        }
    }

    /**
     * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` if there is none.
     * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _upperBinaryLookup(
        Checkpoint[] storage self,
        uint32 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._blockNumber > key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return high;
    }

    /**
     * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or `high` if there is none.
     * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _lowerBinaryLookup(
        Checkpoint[] storage self,
        uint32 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._blockNumber < key) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return high;
    }

    /**
     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
     */
    function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private pure returns (Checkpoint storage result) {
        assembly {
            mstore(0, self.slot)
            result.slot := add(keccak256(0, 0x20), pos)
        }
    }

    struct Trace224 {
        Checkpoint224[] _checkpoints;
    }

    struct Checkpoint224 {
        uint32 _key;
        uint224 _value;
    }

    /**
     * @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint.
     *
     * Returns previous value and new value.
     */
    function push(Trace224 storage self, uint32 key, uint224 value) internal returns (uint224, uint224) {
        return _insert(self._checkpoints, key, value);
    }

    /**
     * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if there is none.
     */
    function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
        uint256 len = self._checkpoints.length;
        uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
        return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
    }

    /**
     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none.
     */
    function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
        uint256 len = self._checkpoints.length;
        uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none.
     *
     * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high keys).
     */
    function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) {
        uint256 len = self._checkpoints.length;

        uint256 low = 0;
        uint256 high = len;

        if (len > 5) {
            uint256 mid = len - Math.sqrt(len);
            if (key < _unsafeAccess(self._checkpoints, mid)._key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);

        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
     */
    function latest(Trace224 storage self) internal view returns (uint224) {
        uint256 pos = self._checkpoints.length;
        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
     * in the most recent checkpoint.
     */
    function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) {
        uint256 pos = self._checkpoints.length;
        if (pos == 0) {
            return (false, 0, 0);
        } else {
            Checkpoint224 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
            return (true, ckpt._key, ckpt._value);
        }
    }

    /**
     * @dev Returns the number of checkpoint.
     */
    function length(Trace224 storage self) internal view returns (uint256) {
        return self._checkpoints.length;
    }

    /**
     * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
     * or by updating the last one.
     */
    function _insert(Checkpoint224[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) {
        uint256 pos = self.length;

        if (pos > 0) {
            // Copying to memory is important here.
            Checkpoint224 memory last = _unsafeAccess(self, pos - 1);

            // Checkpoint keys must be non-decreasing.
            require(last._key <= key, "Checkpoint: decreasing keys");

            // Update or push new checkpoint
            if (last._key == key) {
                _unsafeAccess(self, pos - 1)._value = value;
            } else {
                self.push(Checkpoint224({_key: key, _value: value}));
            }
            return (last._value, value);
        } else {
            self.push(Checkpoint224({_key: key, _value: value}));
            return (0, value);
        }
    }

    /**
     * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` if there is none.
     * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _upperBinaryLookup(
        Checkpoint224[] storage self,
        uint32 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._key > key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return high;
    }

    /**
     * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or `high` if there is none.
     * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _lowerBinaryLookup(
        Checkpoint224[] storage self,
        uint32 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._key < key) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return high;
    }

    /**
     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
     */
    function _unsafeAccess(
        Checkpoint224[] storage self,
        uint256 pos
    ) private pure returns (Checkpoint224 storage result) {
        assembly {
            mstore(0, self.slot)
            result.slot := add(keccak256(0, 0x20), pos)
        }
    }

    struct Trace160 {
        Checkpoint160[] _checkpoints;
    }

    struct Checkpoint160 {
        uint96 _key;
        uint160 _value;
    }

    /**
     * @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint.
     *
     * Returns previous value and new value.
     */
    function push(Trace160 storage self, uint96 key, uint160 value) internal returns (uint160, uint160) {
        return _insert(self._checkpoints, key, value);
    }

    /**
     * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if there is none.
     */
    function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
        uint256 len = self._checkpoints.length;
        uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
        return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
    }

    /**
     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none.
     */
    function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
        uint256 len = self._checkpoints.length;
        uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none.
     *
     * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high keys).
     */
    function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) {
        uint256 len = self._checkpoints.length;

        uint256 low = 0;
        uint256 high = len;

        if (len > 5) {
            uint256 mid = len - Math.sqrt(len);
            if (key < _unsafeAccess(self._checkpoints, mid)._key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);

        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
     */
    function latest(Trace160 storage self) internal view returns (uint160) {
        uint256 pos = self._checkpoints.length;
        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
     * in the most recent checkpoint.
     */
    function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) {
        uint256 pos = self._checkpoints.length;
        if (pos == 0) {
            return (false, 0, 0);
        } else {
            Checkpoint160 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
            return (true, ckpt._key, ckpt._value);
        }
    }

    /**
     * @dev Returns the number of checkpoint.
     */
    function length(Trace160 storage self) internal view returns (uint256) {
        return self._checkpoints.length;
    }

    /**
     * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
     * or by updating the last one.
     */
    function _insert(Checkpoint160[] storage self, uint96 key, uint160 value) private returns (uint160, uint160) {
        uint256 pos = self.length;

        if (pos > 0) {
            // Copying to memory is important here.
            Checkpoint160 memory last = _unsafeAccess(self, pos - 1);

            // Checkpoint keys must be non-decreasing.
            require(last._key <= key, "Checkpoint: decreasing keys");

            // Update or push new checkpoint
            if (last._key == key) {
                _unsafeAccess(self, pos - 1)._value = value;
            } else {
                self.push(Checkpoint160({_key: key, _value: value}));
            }
            return (last._value, value);
        } else {
            self.push(Checkpoint160({_key: key, _value: value}));
            return (0, value);
        }
    }

    /**
     * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` if there is none.
     * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _upperBinaryLookup(
        Checkpoint160[] storage self,
        uint96 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._key > key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return high;
    }

    /**
     * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or `high` if there is none.
     * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _lowerBinaryLookup(
        Checkpoint160[] storage self,
        uint96 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._key < key) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return high;
    }

    /**
     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
     */
    function _unsafeAccess(
        Checkpoint160[] storage self,
        uint256 pos
    ) private pure returns (Checkpoint160 storage result) {
        assembly {
            mstore(0, self.slot)
            result.slot := add(keccak256(0, 0x20), pos)
        }
    }
}

File 14 of 34 : AccessControlDefaultAdminRules.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControlDefaultAdminRules.sol)

pragma solidity ^0.8.0;

import "./AccessControl.sol";
import "./IAccessControlDefaultAdminRules.sol";
import "../utils/math/SafeCast.sol";
import "../interfaces/IERC5313.sol";

/**
 * @dev Extension of {AccessControl} that allows specifying special rules to manage
 * the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions
 * over other roles that may potentially have privileged rights in the system.
 *
 * If a specific role doesn't have an admin role assigned, the holder of the
 * `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.
 *
 * This contract implements the following risk mitigations on top of {AccessControl}:
 *
 * * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.
 * * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.
 * * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.
 * * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.
 * * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.
 *
 * Example usage:
 *
 * ```solidity
 * contract MyToken is AccessControlDefaultAdminRules {
 *   constructor() AccessControlDefaultAdminRules(
 *     3 days,
 *     msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder
 *    ) {}
 * }
 * ```
 *
 * _Available since v4.9._
 */
abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {
    // pending admin pair read/written together frequently
    address private _pendingDefaultAdmin;
    uint48 private _pendingDefaultAdminSchedule; // 0 == unset

    uint48 private _currentDelay;
    address private _currentDefaultAdmin;

    // pending delay pair read/written together frequently
    uint48 private _pendingDelay;
    uint48 private _pendingDelaySchedule; // 0 == unset

    /**
     * @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
     */
    constructor(uint48 initialDelay, address initialDefaultAdmin) {
        require(initialDefaultAdmin != address(0), "AccessControl: 0 default admin");
        _currentDelay = initialDelay;
        _grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC5313-owner}.
     */
    function owner() public view virtual returns (address) {
        return defaultAdmin();
    }

    ///
    /// Override AccessControl role management
    ///

    /**
     * @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
     */
    function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
        require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly grant default admin role");
        super.grantRole(role, account);
    }

    /**
     * @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
     */
    function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
        require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly revoke default admin role");
        super.revokeRole(role, account);
    }

    /**
     * @dev See {AccessControl-renounceRole}.
     *
     * For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling
     * {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule
     * has also passed when calling this function.
     *
     * After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.
     *
     * NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},
     * thereby disabling any functionality that is only available for it, and the possibility of reassigning a
     * non-administrated role.
     */
    function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
            (address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
            require(
                newDefaultAdmin == address(0) && _isScheduleSet(schedule) && _hasSchedulePassed(schedule),
                "AccessControl: only can renounce in two delayed steps"
            );
            delete _pendingDefaultAdminSchedule;
        }
        super.renounceRole(role, account);
    }

    /**
     * @dev See {AccessControl-_grantRole}.
     *
     * For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the
     * role has been previously renounced.
     *
     * NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`
     * assignable again. Make sure to guarantee this is the expected behavior in your implementation.
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        if (role == DEFAULT_ADMIN_ROLE) {
            require(defaultAdmin() == address(0), "AccessControl: default admin already granted");
            _currentDefaultAdmin = account;
        }
        super._grantRole(role, account);
    }

    /**
     * @dev See {AccessControl-_revokeRole}.
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
            delete _currentDefaultAdmin;
        }
        super._revokeRole(role, account);
    }

    /**
     * @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
        require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't violate default admin rules");
        super._setRoleAdmin(role, adminRole);
    }

    ///
    /// AccessControlDefaultAdminRules accessors
    ///

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function defaultAdmin() public view virtual returns (address) {
        return _currentDefaultAdmin;
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
        return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function defaultAdminDelay() public view virtual returns (uint48) {
        uint48 schedule = _pendingDelaySchedule;
        return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
        schedule = _pendingDelaySchedule;
        return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {
        return 5 days;
    }

    ///
    /// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin
    ///

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _beginDefaultAdminTransfer(newAdmin);
    }

    /**
     * @dev See {beginDefaultAdminTransfer}.
     *
     * Internal function without access restriction.
     */
    function _beginDefaultAdminTransfer(address newAdmin) internal virtual {
        uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();
        _setPendingDefaultAdmin(newAdmin, newSchedule);
        emit DefaultAdminTransferScheduled(newAdmin, newSchedule);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _cancelDefaultAdminTransfer();
    }

    /**
     * @dev See {cancelDefaultAdminTransfer}.
     *
     * Internal function without access restriction.
     */
    function _cancelDefaultAdminTransfer() internal virtual {
        _setPendingDefaultAdmin(address(0), 0);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function acceptDefaultAdminTransfer() public virtual {
        (address newDefaultAdmin, ) = pendingDefaultAdmin();
        require(_msgSender() == newDefaultAdmin, "AccessControl: pending admin must accept");
        _acceptDefaultAdminTransfer();
    }

    /**
     * @dev See {acceptDefaultAdminTransfer}.
     *
     * Internal function without access restriction.
     */
    function _acceptDefaultAdminTransfer() internal virtual {
        (address newAdmin, uint48 schedule) = pendingDefaultAdmin();
        require(_isScheduleSet(schedule) && _hasSchedulePassed(schedule), "AccessControl: transfer delay not passed");
        _revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
        _grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
        delete _pendingDefaultAdmin;
        delete _pendingDefaultAdminSchedule;
    }

    ///
    /// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay
    ///

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _changeDefaultAdminDelay(newDelay);
    }

    /**
     * @dev See {changeDefaultAdminDelay}.
     *
     * Internal function without access restriction.
     */
    function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {
        uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);
        _setPendingDelay(newDelay, newSchedule);
        emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRules
     */
    function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _rollbackDefaultAdminDelay();
    }

    /**
     * @dev See {rollbackDefaultAdminDelay}.
     *
     * Internal function without access restriction.
     */
    function _rollbackDefaultAdminDelay() internal virtual {
        _setPendingDelay(0, 0);
    }

    /**
     * @dev Returns the amount of seconds to wait after the `newDelay` will
     * become the new {defaultAdminDelay}.
     *
     * The value returned guarantees that if the delay is reduced, it will go into effect
     * after a wait that honors the previously set delay.
     *
     * See {defaultAdminDelayIncreaseWait}.
     */
    function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {
        uint48 currentDelay = defaultAdminDelay();

        // When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up
        // to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day
        // to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new
        // delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like
        // using milliseconds instead of seconds.
        //
        // When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees
        // that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled.
        // For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.
        return
            newDelay > currentDelay
                ? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48
                : currentDelay - newDelay;
    }

    ///
    /// Private setters
    ///

    /**
     * @dev Setter of the tuple for pending admin and its schedule.
     *
     * May emit a DefaultAdminTransferCanceled event.
     */
    function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {
        (, uint48 oldSchedule) = pendingDefaultAdmin();

        _pendingDefaultAdmin = newAdmin;
        _pendingDefaultAdminSchedule = newSchedule;

        // An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.
        if (_isScheduleSet(oldSchedule)) {
            // Emit for implicit cancellations when another default admin was scheduled.
            emit DefaultAdminTransferCanceled();
        }
    }

    /**
     * @dev Setter of the tuple for pending delay and its schedule.
     *
     * May emit a DefaultAdminDelayChangeCanceled event.
     */
    function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {
        uint48 oldSchedule = _pendingDelaySchedule;

        if (_isScheduleSet(oldSchedule)) {
            if (_hasSchedulePassed(oldSchedule)) {
                // Materialize a virtual delay
                _currentDelay = _pendingDelay;
            } else {
                // Emit for implicit cancellations when another delay was scheduled.
                emit DefaultAdminDelayChangeCanceled();
            }
        }

        _pendingDelay = newDelay;
        _pendingDelaySchedule = newSchedule;
    }

    ///
    /// Private helpers
    ///

    /**
     * @dev Defines if an `schedule` is considered set. For consistency purposes.
     */
    function _isScheduleSet(uint48 schedule) private pure returns (bool) {
        return schedule != 0;
    }

    /**
     * @dev Defines if an `schedule` is considered passed. For consistency purposes.
     */
    function _hasSchedulePassed(uint48 schedule) private view returns (bool) {
        return schedule < block.timestamp;
    }
}

File 15 of 34 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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 Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

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

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

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        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 16 of 34 : IPausable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IPausable {
  /// @notice This function pauses the contract
  /// @dev Sets the pause flag to true
  function emergencyPause() external;

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

File 17 of 34 : MerkleProof.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.2) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * 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.
 * OpenZeppelin's JavaScript library generates merkle trees that are safe
 * against this attack out of the box.
 */
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 Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(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++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            require(proofPos == proofLen, "MerkleProof: invalid multiproof");
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            require(proofPos == proofLen, "MerkleProof: invalid multiproof");
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 18 of 34 : IMerkleAccessController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IMerkleAccessController {
  /// @notice Emitted when the contract owner updates the staking allowlist
  /// @param oldMerkleRoot The root of the old Staking allowlist merkle tree
  /// @param newMerkleRoot The root of a new Staking allowlist merkle tree
  event MerkleRootChanged(bytes32 oldMerkleRoot, 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
  /// @return true If the staker has access to the private staking pool
  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 opening but can be removed at any time by the owner when
  /// staking access will be granted to the public.
  function setMerkleRoot(bytes32 newMerkleRoot) external;

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

File 19 of 34 : StakingPoolBase.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {ERC677ReceiverInterface} from
  "@chainlink/contracts/src/v0.8/interfaces/ERC677ReceiverInterface.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {Checkpoints} from "@openzeppelin/contracts/utils/Checkpoints.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {IMigratable} from "../interfaces/IMigratable.sol";
import {IRewardVault} from "../interfaces/IRewardVault.sol";
import {IStakingOwner} from "../interfaces/IStakingOwner.sol";
import {IStakingPool} from "../interfaces/IStakingPool.sol";
import {Migratable} from "../Migratable.sol";
import {PausableWithAccessControl} from "../PausableWithAccessControl.sol";

/// @notice This contract is the base contract for staking pools. Each staking pool extends this
/// contract.
/// @dev This contract is abstract and must be inherited.
/// @dev invariant maxPoolSize must be greater than or equal to the totalPrincipal.
/// @dev invariant maxPoolSize must be greater than or equal to the maxPrincipalPerStaker.
/// @dev invariant contract's LINK token balance should be greater than or equal to the
/// totalPrincipal.
/// @dev invariant The migrated staked LINK amount must be less than or equal to the staker's staked
/// LINK amount +
/// rewards from the v0.1 staking pool.
/// @dev invariant The migrated staked LINK amount must be less than or equal to the
/// maxPrincipalPerStaker.
/// @dev We only support LINK token in v0.2 staking. Rebasing tokens, ERC777 tokens, fee-on-transfer
/// tokens or tokens that do not have 18 decimal places are not supported.
abstract contract StakingPoolBase is
  ERC677ReceiverInterface,
  IStakingPool,
  IStakingOwner,
  Migratable,
  PausableWithAccessControl
{
  using Checkpoints for Checkpoints.History;
  using SafeCast for uint256;

  /// @notice This error is thrown when the staking pool is not active.
  error PoolNotActive();

  /// @notice This error is thrown when the unbonding period is set to 0
  error InvalidUnbondingPeriod();

  /// @notice This error is thrown when the claim period is set to 0
  error InvalidClaimPeriod();

  /// @notice This error is thrown whenever a staker tries to unbond during
  /// their unbonding period.
  /// @param unbondingPeriodEndsAt The time the unbonding period is finished
  error UnbondingOrClaimPeriodActive(uint256 unbondingPeriodEndsAt);

  /// @notice This error is thrown whenever a staker tries to unstake outside
  /// the claim period
  /// @param staker The staker trying to unstake
  error StakerNotInClaimPeriod(address staker);

  /// @notice This error is thrown when an invalid claim period range is provided
  /// @param minClaimPeriod The min claim period
  /// @param maxClaimPeriod The max claim period
  error InvalidClaimPeriodRange(uint256 minClaimPeriod, uint256 maxClaimPeriod);

  /// @notice This error is thrown when an invalid max unbonding period is provided
  /// @param maxUnbondingPeriod The max unbonding period
  error InvalidMaxUnbondingPeriod(uint256 maxUnbondingPeriod);

  /// @notice This error is thrown when a staker tries to stake and the reward vault connected to
  /// this pool is not open or is paused
  error RewardVaultNotActive();

  /// @notice This error is thrown when admin tries to open the pool and the reward vault connected
  /// to this pool has not had rewards added to it.
  error RewardVaultHasNoRewards();

  /// @notice This error is thrown when admin tries to set a new reward vault and the old reward
  /// vault is not closed yet.
  error RewardVaultNotClosed();

  /// @notice This event is emitted whenever a staker initiates the unbonding
  /// period.
  /// @param staker The staker that has started their unbonding period.
  event UnbondingPeriodStarted(address indexed staker);

  /// @notice This event is emitted when a staker's unbonding period is reset
  /// @param staker The staker that has reset their unbonding period
  event UnbondingPeriodReset(address indexed staker);

  /// @notice This event is emitted when the unbonding period has been changed
  /// @param oldUnbondingPeriod The old unbonding period
  /// @param newUnbondingPeriod The new unbonding period
  event UnbondingPeriodSet(uint256 oldUnbondingPeriod, uint256 newUnbondingPeriod);

  /// @notice This event is emitted when the claim period is set
  /// @param oldClaimPeriod The old claim period
  /// @param newClaimPeriod The new claim period
  event ClaimPeriodSet(uint256 oldClaimPeriod, uint256 newClaimPeriod);

  /// @notice This event is emitted when the reward vault is set
  /// @param oldRewardVault The old reward vault
  /// @param newRewardVault The new reward vault
  event RewardVaultSet(address indexed oldRewardVault, address indexed newRewardVault);

  /// @notice This event is emitted when the staker is migrated to the migration target
  /// @param migrationTarget The migration target
  /// @param amount The staker's staked LINK amount that was migrated in juels
  /// @param migrationData The migration data
  event StakerMigrated(address indexed migrationTarget, uint256 amount, bytes migrationData);

  /// @notice This struct defines the params required by the Staking contract's
  /// constructor.
  struct ConstructorParamsBase {
    /// @notice The LINK Token
    LinkTokenInterface LINKAddress;
    /// @notice The initial maximum total stake amount for all stakers in the
    /// pool
    uint96 initialMaxPoolSize;
    /// @notice The initial maximum stake amount for a staker
    uint96 initialMaxPrincipalPerStaker;
    /// @notice The minimum stake amount that a staker must stake
    uint96 minPrincipalPerStaker;
    /// @notice The initial unbonding period
    uint32 initialUnbondingPeriod;
    /// @notice The max value that the unbonding period can be set to
    uint32 maxUnbondingPeriod;
    /// @notice The initial claim period
    uint32 initialClaimPeriod;
    /// @notice The min value that the claim period can be set to
    uint32 minClaimPeriod;
    /// @notice The max value that the claim period can be set to
    uint32 maxClaimPeriod;
    /// @notice The time it requires to transfer admin role
    uint48 adminRoleTransferDelay;
  }

  /// @notice This struct defines the params that the pool is configured with
  struct PoolConfigs {
    /// @notice The max amount of staked LINK allowed in the pool in juels. The max value of this
    /// field is expected to be less than 1 billion (10^9 * 10^18), which is less than the max value
    /// that can be represented by a uint96 (~7.9*10^28).
    uint96 maxPoolSize;
    /// @notice The max amount of LINK a staker can stake in juels. The max value of this field is
    /// expected to be less than 1 million (10^6 * 10^18), which is less than the max value that can
    /// be represented by a uint96 (~7.9*10^28).
    uint96 maxPrincipalPerStaker;
    /// @notice The length of the unbonding period in seconds. The max value of this field is
    /// expected to be less than a year, or 30 million (3.2*10^7), which is less than the max value
    /// that can be represented by a uint32 (~4.2*10^9).
    uint32 unbondingPeriod;
    /// @notice The length of the claim period in seconds. The max value of this field is
    /// expected to be less than a year, or 30 million (3.2*10^7), which is less than the max value
    /// that can be represented by a uint32 (~4.2*10^9).
    uint32 claimPeriod;
  }

  /// @notice This struct defines the state of the staking pool
  struct PoolState {
    /// @notice The total staked LINK amount amount in the pool
    uint256 totalPrincipal;
    /// @notice The time that the pool was closed
    uint256 closedAt;
  }

  /// @notice This struct defines the global state and configuration of the pool
  struct Pool {
    /// @notice The pool's configuration
    PoolConfigs configs;
    /// @notice The pool's state
    PoolState state;
  }

  /// @notice This is the ID for the initiator role, which is given to the
  /// addresses that will add open the pools, and set the merkle root for the community pool.
  /// @dev Hash: 6b8b15f1c11543d8280deaa7c24d12fffba6a357e4428e8c43e4234790186bff
  bytes32 public constant INITIATOR_ROLE = keccak256("INITIATOR_ROLE");
  /// @notice The LINK token
  LinkTokenInterface internal immutable i_LINK;
  /// @notice The staking pool state and configuration
  Pool internal s_pool;
  /// @notice Mapping of a staker's address to their staker state
  mapping(address staker => IStakingPool.Staker) internal s_stakers;
  /// @notice Migration proxy address
  address internal s_migrationProxy;
  /// @notice The latest reward vault address
  IRewardVault internal s_rewardVault;
  /// @notice The min amount of LINK that a staker can stake
  uint96 internal immutable i_minPrincipalPerStaker;
  /// @notice The min value that the claim period can be set to
  uint32 private immutable i_minClaimPeriod;
  /// @notice The max value that the claim period can be set to
  uint32 private immutable i_maxClaimPeriod;
  /// @notice The max value that the unbonding period can be set to
  uint32 private immutable i_maxUnbondingPeriod;
  /// @notice Flag that signals if the staking pool is open for staking
  bool internal s_isOpen;

  constructor(ConstructorParamsBase memory params)
    PausableWithAccessControl(params.adminRoleTransferDelay, msg.sender)
  {
    if (address(params.LINKAddress) == address(0)) revert InvalidZeroAddress();
    if (params.minPrincipalPerStaker == 0) revert InvalidMinStakeAmount();
    if (params.minPrincipalPerStaker >= params.initialMaxPrincipalPerStaker) {
      revert InvalidMinStakeAmount();
    }
    if (params.maxUnbondingPeriod == 0) {
      revert InvalidMaxUnbondingPeriod(params.maxUnbondingPeriod);
    }
    if (params.minClaimPeriod == 0 || params.minClaimPeriod >= params.maxClaimPeriod) {
      revert InvalidClaimPeriodRange(params.minClaimPeriod, params.maxClaimPeriod);
    }

    i_LINK = params.LINKAddress;
    i_minPrincipalPerStaker = params.minPrincipalPerStaker;

    i_maxUnbondingPeriod = params.maxUnbondingPeriod;
    _setUnbondingPeriod(params.initialUnbondingPeriod);

    _setPoolConfig(params.initialMaxPoolSize, params.initialMaxPrincipalPerStaker);

    i_minClaimPeriod = params.minClaimPeriod;
    i_maxClaimPeriod = params.maxClaimPeriod;
    _setClaimPeriod(params.initialClaimPeriod);
  }

  /// @inheritdoc IMigratable
  /// @dev This will migrate the staker's staked LINK
  /// @dev precondition This contract must be closed and upgraded to a new pool.
  /// @dev precondition The migration target must be set.
  /// @dev precondition The caller must be staked in the pool.
  function migrate(bytes calldata data) external whenClosed validateMigrationTargetSet {
    // must be in storage to get access to latest()
    IStakingPool.Staker storage staker = s_stakers[msg.sender];

    uint224 history = staker.history.latest();
    uint112 stakerPrincipal = uint112(history >> 112);
    uint112 stakerStakedAtTime = uint112(history);
    if (stakerPrincipal == 0) revert StakeNotFound(msg.sender);

    bytes memory migrationData = abi.encode(msg.sender, stakerStakedAtTime, data);

    // Finalize staker's rewards to include any rewards they have earned before resetting the
    // principal and stakedAtTime.
    s_rewardVault.concludeRewardPeriod({
      staker: msg.sender,
      oldPrincipal: stakerPrincipal,
      stakedAt: stakerStakedAtTime,
      unstakedAmount: stakerPrincipal,
      shouldForfeit: false
    });
    s_pool.state.totalPrincipal -= stakerPrincipal;

    // do not reset staked at time to not reset the multiplier because staker is not forfeiting
    // rewards when migrating
    _updateStakerHistory({
      staker: staker,
      latestPrincipal: 0,
      latestStakedAtTime: stakerStakedAtTime
    });
    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transferAndCall({to: s_migrationTarget, value: stakerPrincipal, data: migrationData});
    emit StakerMigrated(s_migrationTarget, stakerPrincipal, migrationData);
  }

  /// @notice Starts the unbonding period for the staker.  A staker may unstake
  /// their staked LINK during the claim period that follows the unbonding period.
  /// @dev precondition The caller must be staked in the pool.
  /// @dev precondition The caller must not be in an unbonding period.
  /// @dev precondition The caller must not be in a claim period.
  function unbond() external virtual {
    Staker storage staker = s_stakers[msg.sender];
    uint224 history = staker.history.latest();
    uint112 stakerPrincipal = uint112(history >> 112);
    if (stakerPrincipal == 0) revert StakeNotFound(msg.sender);

    _unbond(staker);
  }

  /// @notice Sets the new unbonding period for the pool.  Stakers that are
  /// already unbonding will not be affected.
  /// @param newUnbondingPeriod The new unbonding period
  /// @dev precondition The caller must have the default admin role.
  /// @dev precondition Cannot be called after the pool is closed.
  function setUnbondingPeriod(uint256 newUnbondingPeriod)
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
    whenBeforeClosing
  {
    _setUnbondingPeriod(newUnbondingPeriod);
  }

  /// @notice Returns the max unbonding period
  /// @return uint256 The max value that the unbonding period can be set to
  function getMaxUnbondingPeriod() external view returns (uint256) {
    return (i_maxUnbondingPeriod);
  }

  /// @notice Set the claim period
  /// @param claimPeriod The claim period
  /// @dev precondition Cannot be called after the pool is closed.
  function setClaimPeriod(uint256 claimPeriod)
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
    whenBeforeClosing
  {
    _setClaimPeriod(claimPeriod);
  }

  /// @notice Sets the new reward vault for the pool
  /// @param newRewardVault The new reward vault
  /// @dev precondition The caller must have the default admin role.
  /// @dev precondition Cannot be called after the pool is closed.
  function setRewardVault(IRewardVault newRewardVault)
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
    whenBeforeClosing
  {
    if (address(newRewardVault) == address(0)) revert InvalidZeroAddress();
    address oldRewardVault = address(s_rewardVault);
    if (oldRewardVault == address(newRewardVault)) return;
    if (address(s_rewardVault) != address(0) && s_rewardVault.isOpen()) {
      revert RewardVaultNotClosed();
    }
    if (
      address(s_rewardVault) != address(0)
        && (!newRewardVault.isOpen() || newRewardVault.isPaused())
    ) revert RewardVaultNotActive();
    if (address(s_rewardVault) != address(0) && !newRewardVault.hasRewardAdded()) {
      revert RewardVaultHasNoRewards();
    }

    s_rewardVault = newRewardVault;
    emit RewardVaultSet(oldRewardVault, address(newRewardVault));
  }

  /// @notice LINK transfer callback function called when transferAndCall is called with this
  /// contract as a target.
  /// @param sender staker's address if they stake into the pool by calling transferAndCall on the
  /// LINK token, or MigrationProxy contract when a staker migrates from V0.1 to V0.2
  /// @param amount Amount of LINK token transferred
  /// @param data Bytes data received, represents migration path
  /// @inheritdoc ERC677ReceiverInterface
  /// @dev precondition The migration proxy must be set.
  /// @dev precondition This contract must be open and not paused.
  /// @dev precondition The reward vault must be open and not paused.
  function onTokenTransfer(
    address sender,
    uint256 amount,
    bytes calldata data
  ) external validateFromLINK validateMigrationProxySet whenOpen whenRewardVaultOpen whenNotPaused {
    if (amount == 0) return;

    // Check if this call was forwarded from the migration proxy.
    address staker = sender == s_migrationProxy ? _getStakerAddress(data) : sender;
    if (staker == address(0)) revert InvalidZeroAddress();

    // includes access check for non migration proxy
    _validateOnTokenTransfer(sender, staker, data);

    Staker storage stakerState = s_stakers[staker];
    uint224 history = stakerState.history.latest();
    uint256 stakerPrincipal = uint256(history >> 112);
    uint256 stakedAt = uint112(history);

    _resetUnbondingPeriod(stakerState, staker);

    s_rewardVault.concludeRewardPeriod({
      staker: staker,
      oldPrincipal: stakerPrincipal,
      unstakedAmount: 0,
      shouldForfeit: false,
      stakedAt: stakedAt
    });

    _increaseStake(staker, stakerPrincipal + amount, amount);
  }

  /// @notice Returns the minimum and maximum claim periods that can be set by the owner
  /// @return uint256 minimum claim period
  /// @return uint256 maximum claim period
  function getClaimPeriodLimits() external view returns (uint256, uint256) {
    return (i_minClaimPeriod, i_maxClaimPeriod);
  }

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

  /// @inheritdoc IStakingOwner
  /// @dev precondition The caller must have the default admin role.
  function setPoolConfig(
    uint256 maxPoolSize,
    uint256 maxPrincipalPerStaker
  ) external virtual onlyRole(DEFAULT_ADMIN_ROLE) whenOpen {
    _setPoolConfig(maxPoolSize, maxPrincipalPerStaker);
  }

  /// @inheritdoc IStakingOwner
  /// @dev precondition The caller must have the initiator role.
  function open()
    external
    onlyRole(INITIATOR_ROLE)
    whenBeforeOpening
    validateRewardVaultSet
    whenRewardVaultOpen
    whenRewardVaultHasRewards
  {
    _validateBeforeOpen();
    s_isOpen = true;
    emit PoolOpened();
  }

  /// @inheritdoc IStakingOwner
  /// @dev precondition The caller must have the default admin role.
  function close() external onlyRole(DEFAULT_ADMIN_ROLE) whenOpen {
    s_isOpen = false;
    s_pool.state.closedAt = block.timestamp;
    emit PoolClosed();
  }

  /// @inheritdoc IStakingOwner
  /// @dev precondition The caller must have the default admin role.
  function setMigrationProxy(address migrationProxy)
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
    whenBeforeClosing
  {
    if (migrationProxy == address(0)) revert InvalidZeroAddress();

    if (s_migrationProxy == migrationProxy) return;
    address oldMigrationProxy = s_migrationProxy;
    s_migrationProxy = migrationProxy;

    emit MigrationProxySet(oldMigrationProxy, migrationProxy);
  }

  // =================
  // IStakingPool
  // =================

  /// @inheritdoc IStakingPool
  /// @dev precondition The caller must be staked in the pool.
  /// @dev precondition The caller must be in the claim period or the pool must be closed or paused.
  /// @dev There is a possible reentrancy attack here where a malicious admin
  /// can point this pool to a malicious reward vault that calls unstake on the
  /// pool again.  This reentrancy attack is possible as the pool updates the
  /// staker's staked LINK amount after it calls concludeRewardPeriod on the configured reward
  /// vault.  This scenario is mitigated by forcing the admin to go through
  /// a timelock period that is longer than the unbonding period, which will
  /// provide stakers sufficient time to withdraw their staked LINK from the
  /// pool before a malicious reward vault is set.
  function unstake(uint256 amount) external {
    // cannot unstake 0
    if (amount == 0) revert UnstakeZeroAmount();

    Staker storage staker = s_stakers[msg.sender];
    if (!_canUnstake(staker)) {
      revert StakerNotInClaimPeriod(msg.sender);
    }

    uint224 history = staker.history.latest();
    uint256 stakerPrincipal = uint256(history >> 112);
    uint256 stakedAt = uint112(history);
    // verify that the staker has enough staked LINK amount to unstake
    if (amount > stakerPrincipal) revert UnstakeExceedsPrincipal();

    uint256 updatedPrincipal = stakerPrincipal - amount;
    // in the case of a partial withdrawal, verify new staked LINK amount is above minimum
    if (amount < stakerPrincipal && updatedPrincipal < i_minPrincipalPerStaker) {
      revert UnstakePrincipalBelowMinAmount();
    }

    s_rewardVault.concludeRewardPeriod({
      staker: msg.sender,
      oldPrincipal: stakerPrincipal,
      unstakedAmount: amount,
      shouldForfeit: true,
      stakedAt: stakedAt
    });

    s_pool.state.totalPrincipal -= amount;

    // Reset the staker's staked at time to 0 to prevent the multiplier
    // from growing if the staker has unstaked all their staked LINK
    _updateStakerHistory({
      staker: staker,
      latestPrincipal: updatedPrincipal,
      latestStakedAtTime: updatedPrincipal == 0 ? 0 : block.timestamp
    });
    // The return value is not checked since the call will revert if any balance, allowance or
    // receiver conditions fail.
    i_LINK.transfer(msg.sender, amount);

    emit Unstaked(msg.sender, amount, updatedPrincipal, s_pool.state.totalPrincipal);
  }

  /// @inheritdoc IStakingPool
  function getTotalPrincipal() external view returns (uint256) {
    return s_pool.state.totalPrincipal;
  }

  /// @inheritdoc IStakingPool
  function getStakerPrincipal(address staker) external view returns (uint256) {
    return uint112(s_stakers[staker].history.latest() >> 112);
  }

  /// @inheritdoc IStakingPool
  function getStakerPrincipalAt(
    address staker,
    uint256 blockNumber
  ) external view returns (uint256) {
    // `Checkpoints` requires to exclude the current block when calling `getAtBlock`
    return (blockNumber == block.number)
      ? uint112(s_stakers[staker].history.latest() >> 112)
      : uint112(s_stakers[staker].history.getAtBlock(blockNumber) >> 112);
  }

  /// @inheritdoc IStakingPool
  function getStakerStakedAtTime(address staker) external view returns (uint256) {
    return uint112(s_stakers[staker].history.latest());
  }

  /// @inheritdoc IStakingPool
  function getStakerStakedAtTimeAt(
    address staker,
    uint256 blockNumber
  ) external view returns (uint256) {
    // `Checkpoints` requires to exclude the current block when calling `getAtBlock`
    return (blockNumber == block.number)
      ? uint112(s_stakers[staker].history.latest())
      : uint112(s_stakers[staker].history.getAtBlock(blockNumber));
  }

  /// @inheritdoc IStakingPool
  function getRewardVault() external view returns (IRewardVault) {
    return s_rewardVault;
  }

  /// @inheritdoc IStakingPool
  function getChainlinkToken() external view returns (address) {
    return address(i_LINK);
  }

  /// @inheritdoc IStakingPool
  function getMigrationProxy() external view returns (address) {
    return s_migrationProxy;
  }

  /// @inheritdoc IStakingPool
  function isOpen() external view returns (bool) {
    return s_isOpen;
  }

  /// @inheritdoc IStakingPool
  function isActive() external view returns (bool) {
    return _isActive();
  }

  /// @inheritdoc IStakingPool
  function getStakerLimits() external view returns (uint256, uint256) {
    return (i_minPrincipalPerStaker, s_pool.configs.maxPrincipalPerStaker);
  }

  /// @inheritdoc IStakingPool
  function getMaxPoolSize() external view returns (uint256) {
    return s_pool.configs.maxPoolSize;
  }

  /// @notice Returns the time a staker's unbonding period ends
  /// @param staker The address of the staker to query
  /// @return uint256 The timestamp of when the staker's unbonding period ends.
  /// This value will be 0 if the unbonding period is not active.
  function getUnbondingEndsAt(address staker) external view returns (uint256) {
    return s_stakers[staker].unbondingPeriodEndsAt;
  }

  /// @notice Returns the pool's unbonding parameters
  /// @return uint256 The pool's unbonding period
  /// @return uint256 The pools's claim period
  function getUnbondingParams() external view returns (uint256, uint256) {
    return (s_pool.configs.unbondingPeriod, s_pool.configs.claimPeriod);
  }

  /// @notice Returns the time a staker's claim period ends
  /// @param staker The staker trying to unstake their staked LINK
  /// @return uint256 The timestamp of when the staker's claim period ends.
  /// This value will be 0 if the unbonding period has not started.
  function getClaimPeriodEndsAt(address staker) external view returns (uint256) {
    return s_stakers[staker].claimPeriodEndsAt;
  }

  // ===============
  // ERC165
  // ===============

  /// @notice This function allows the calling contract to
  /// check if the contract deployed at this address is a valid
  /// LINKTokenReceiver.  A contract is a valid LINKTokenReceiver
  /// if it implements the onTokenTransfer function.
  /// @param interfaceID The ID of the interface to check against
  /// @return bool True if the contract is a valid LINKTokenReceiver.
  function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
    return interfaceID == this.onTokenTransfer.selector || super.supportsInterface(interfaceID);
  }

  // =========
  // Helpers
  // =========

  /// @notice Resets a staker's unbonding period
  /// @param stakerState The staker's current state
  /// @param staker The address of the staker to reset the unbonding period for
  /// @dev This sets the stakerState's unbondingPeriodEndsAt and
  /// claimPeriodEndsAt to 0
  function _resetUnbondingPeriod(Staker storage stakerState, address staker) internal {
    if (stakerState.unbondingPeriodEndsAt != 0) {
      delete stakerState.unbondingPeriodEndsAt;
      delete stakerState.claimPeriodEndsAt;
      emit UnbondingPeriodReset(staker);
    }
  }

  /// @inheritdoc Migratable
  /// @dev precondition The migration target must implement the onTokenTransfer function.
  /// @dev precondition Cannot be called after the pool is closed.
  function _validateMigrationTarget(address newMigrationTarget) internal override whenBeforeClosing {
    Migratable._validateMigrationTarget(newMigrationTarget);
    if (
      !IERC165(newMigrationTarget).supportsInterface(
        ERC677ReceiverInterface.onTokenTransfer.selector
      )
    ) {
      revert InvalidMigrationTarget();
    }
  }

  /// @notice Validate for when LINK is staked or migrated into the pool
  /// @param sender The address transferring LINK into the pool. Could be the migration proxy
  /// contract or the staker.
  /// @param staker The address staking or migrating LINK into the pool
  /// @param data Arbitrary data passed when staking or migrating
  function _validateOnTokenTransfer(
    address sender,
    address staker,
    bytes calldata data
  ) internal view virtual;

  /// @notice Validates pool state before opening
  function _validateBeforeOpen() internal view virtual;

  /// @notice Util function for setting the pool config
  /// @param maxPoolSize The max amount of staked LINK allowed in the pool
  /// @param maxPrincipalPerStaker The max amount of LINK a staker can stake
  /// in the pool.
  function _setPoolConfig(uint256 maxPoolSize, uint256 maxPrincipalPerStaker) internal {
    PoolConfigs storage configs = s_pool.configs;
    // only allow increasing the maxPoolSize
    if (maxPoolSize == 0 || maxPoolSize < configs.maxPoolSize) {
      revert InvalidPoolSize(maxPoolSize);
    }
    // only allow increasing the maxPrincipalPerStaker
    if (
      maxPrincipalPerStaker == 0 || maxPrincipalPerStaker > maxPoolSize
        || configs.maxPrincipalPerStaker > maxPrincipalPerStaker
    ) revert InvalidMaxStakeAmount(maxPrincipalPerStaker);

    if (configs.maxPoolSize != maxPoolSize) {
      configs.maxPoolSize = maxPoolSize.toUint96();
      emit PoolSizeIncreased(maxPoolSize);
    }
    if (configs.maxPrincipalPerStaker != maxPrincipalPerStaker) {
      configs.maxPrincipalPerStaker = maxPrincipalPerStaker.toUint96();
      emit MaxPrincipalAmountIncreased(maxPrincipalPerStaker);
    }
  }

  /// @notice Util function for setting the unbonding period
  /// @param unbondingPeriod The unbonding period
  function _setUnbondingPeriod(uint256 unbondingPeriod) internal {
    if (unbondingPeriod == 0 || unbondingPeriod > i_maxUnbondingPeriod) {
      revert InvalidUnbondingPeriod();
    }

    if (s_pool.configs.unbondingPeriod == unbondingPeriod) return;

    uint256 oldUnbondingPeriod = s_pool.configs.unbondingPeriod;
    s_pool.configs.unbondingPeriod = unbondingPeriod.toUint32();
    emit UnbondingPeriodSet(oldUnbondingPeriod, unbondingPeriod);
  }

  /// @notice Updates the staking pool state and the staker state
  /// @param sender The staker address
  /// @param newPrincipal The staker's staked LINK amount after staking
  /// @param amount The amount to stake
  function _increaseStake(address sender, uint256 newPrincipal, uint256 amount) internal {
    Staker storage staker = s_stakers[sender];

    // validate staking limits
    if (newPrincipal < i_minPrincipalPerStaker) {
      revert InsufficientStakeAmount();
    }
    if (newPrincipal > s_pool.configs.maxPrincipalPerStaker) {
      revert ExceedsMaxStakeAmount();
    }
    uint256 newTotalPrincipal = s_pool.state.totalPrincipal + amount;
    if (newTotalPrincipal > s_pool.configs.maxPoolSize) {
      revert ExceedsMaxPoolSize();
    }

    // update the pool state
    s_pool.state.totalPrincipal = newTotalPrincipal;

    // update the staker state
    _updateStakerHistory({
      staker: staker,
      latestPrincipal: newPrincipal,
      latestStakedAtTime: block.timestamp
    });

    emit Staked(sender, amount, newPrincipal, newTotalPrincipal);
  }

  /// @notice Gets the staker address from the data passed by the MigrationProxy contract
  /// @param data The data passed by the MigrationProxy contract
  /// @return The staker address
  function _getStakerAddress(bytes calldata data) internal pure returns (address) {
    if (data.length == 0) revert InvalidData();

    // decode the data
    (address staker) = abi.decode(data, (address));

    return staker;
  }

  /// @notice Checks to see whether or not a staker is eligible to
  /// unstake their staked LINK amount (when the pool is closed or, when the pool is open and they
  /// are in the claim period or, when pool is paused)
  /// @param staker The staker trying to unstake their staked LINK
  /// @return bool True if the staker is eligible to unstake
  function _canUnstake(Staker storage staker) internal view returns (bool) {
    return s_pool.state.closedAt != 0 || _inClaimPeriod(staker) || paused();
  }

  /// @notice Updates the staker's staked LINK amount history
  /// @param staker The staker to update
  /// @param latestPrincipal The staker's latest staked LINK amount
  /// @param latestStakedAtTime The staker's latest average staked at time
  function _updateStakerHistory(
    Staker storage staker,
    uint256 latestPrincipal,
    uint256 latestStakedAtTime
  ) internal {
    staker.history.push(
      (uint224(uint112(latestPrincipal)) << 112) | uint224(uint112(latestStakedAtTime))
    );
  }

  /// @notice Starts the unbonding period for the staker
  /// @param staker The staker trying to unbond
  function _unbond(Staker storage staker) internal {
    if (staker.unbondingPeriodEndsAt != 0 && block.timestamp <= staker.claimPeriodEndsAt) {
      revert UnbondingOrClaimPeriodActive(staker.unbondingPeriodEndsAt);
    }
    staker.unbondingPeriodEndsAt = (block.timestamp + s_pool.configs.unbondingPeriod).toUint128();
    staker.claimPeriodEndsAt = staker.unbondingPeriodEndsAt + s_pool.configs.claimPeriod;
    emit UnbondingPeriodStarted(msg.sender);
  }

  /// @notice Checks to see whether or not a staker is within the claim period
  /// to unstake their staked LINK
  /// @param staker The staker trying to unstake their staked LINK
  /// @return bool True if the staker is inside the claim period
  function _inClaimPeriod(Staker storage staker) private view returns (bool) {
    if (staker.unbondingPeriodEndsAt == 0 || block.timestamp < staker.unbondingPeriodEndsAt) {
      return false;
    }

    return block.timestamp <= staker.claimPeriodEndsAt;
  }

  /// @notice Util function for setting the claim period
  /// @param claimPeriod The claim period
  function _setClaimPeriod(uint256 claimPeriod) private {
    if (claimPeriod < i_minClaimPeriod || claimPeriod > i_maxClaimPeriod) {
      revert InvalidClaimPeriod();
    }

    if (s_pool.configs.claimPeriod == claimPeriod) return;

    uint256 oldClaimPeriod = s_pool.configs.claimPeriod;
    s_pool.configs.claimPeriod = claimPeriod.toUint32();

    emit ClaimPeriodSet(oldClaimPeriod, claimPeriod);
  }

  /// @notice Util function to check if the reward vault connected to this pool has rewards added to
  /// it
  /// @return bool True if the reward vault has rewards added to it, false otherwise
  function _hasRewardVaultRewardAdded() internal view virtual returns (bool) {
    return s_rewardVault.hasRewardAdded();
  }

  /// @notice Util function to check if the pool is active
  /// @return bool True if the pool is active, false otherwise
  function _isActive() internal view returns (bool) {
    return s_isOpen && !s_rewardVault.hasRewardDurationEnded(address(this));
  }

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

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

  /// @dev Reverts if migration proxy is not set
  modifier validateMigrationProxySet() {
    if (s_migrationProxy == address(0)) revert MigrationProxyNotSet();
    _;
  }

  /// @dev Reverts if reward vault is not set
  modifier validateRewardVaultSet() {
    if (address(s_rewardVault) == address(0)) revert RewardVaultNotSet();
    _;
  }

  /// @dev Reverts if pool is after an opening
  modifier whenBeforeOpening() {
    if (s_isOpen) revert PoolHasBeenOpened();
    if (s_pool.state.closedAt != 0) revert PoolHasBeenClosed();
    _;
  }

  /// @dev Reverts if the pool is already closed
  modifier whenBeforeClosing() {
    if (s_pool.state.closedAt != 0) revert PoolHasBeenClosed();
    _;
  }

  /// @dev Reverts if pool is not open
  modifier whenOpen() {
    if (!s_isOpen) revert PoolNotOpen();
    _;
  }

  /// @dev Reverts if pool is not active (is open and rewards are available for this pool)
  modifier whenActive() {
    if (!_isActive()) revert PoolNotActive();
    _;
  }

  /// @dev Reverts if pool is not closed
  modifier whenClosed() {
    if (s_pool.state.closedAt == 0) revert PoolNotClosed();
    _;
  }

  /// @dev Reverts if reward vault is not open or is paused
  modifier whenRewardVaultOpen() {
    if (!s_rewardVault.isOpen() || s_rewardVault.isPaused()) revert RewardVaultNotActive();
    _;
  }

  /// @dev Reverts if reward vault has not had rewards added to it
  modifier whenRewardVaultHasRewards() {
    if (!_hasRewardVaultRewardAdded()) revert RewardVaultHasNoRewards();
    _;
  }
}

File 20 of 34 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 21 of 34 : ISlashable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface ISlashable {
  /// @notice This error is thrown when the slasher config is invalid
  error InvalidSlasherConfig();

  /// @notice This error is thrown when the contract manager tries to set the slasher role directly
  /// through
  /// `grantRole`
  error InvalidRole();

  /// @notice This error is thrown then the contract manager tries to set the slasher config for an
  /// address
  /// that doesn't have the slasher role
  error InvalidSlasher();

  /// @notice This struct defines the parameters of the slasher config
  struct SlasherConfig {
    /// @notice The pool's refill rate (Juels/sec)
    uint256 refillRate;
    /// @notice The refillable slash capacity amount
    uint256 slashCapacity;
  }

  /// @notice This struct defines the parameters of the slasher state
  struct SlasherState {
    /// @notice The last slash timestamp, will be 0 if never slashed
    /// The timestamp will be set to the time the slashing configuration was configured
    /// instead of 0 if slashing never occurs, refilling slash capacity to full.
    uint256 lastSlashTimestamp;
    /// @notice The current amount of remaining slash capacity
    uint256 remainingSlashCapacityAmount;
  }

  /// @notice This struct defines the slasher's state and config
  struct Slasher {
    /// @notice The slasher's config
    SlasherConfig config;
    /// @notice The slasher's state
    SlasherState state;
  }

  /// @notice Adds a new slasher with the given config
  /// @param slasher The address of the slasher
  /// @param config The slasher config
  function addSlasher(address slasher, SlasherConfig calldata config) external;

  /// @notice Removes a slasher by revoking the SLASHER_ROLE and resetting the slasher config
  /// @param slasher The address of the slasher
  function removeSlasher(address slasher) external;

  /// @notice Sets the slasher config
  /// @param slasher The address of the slasher
  /// @param config The slasher config
  function setSlasherConfig(address slasher, SlasherConfig calldata config) external;

  /// @notice Returns the slasher config
  /// @param slasher The slasher
  /// @return The slasher config
  function getSlasherConfig(address slasher) external view returns (SlasherConfig memory);

  /// @notice Returns the slash capacity for a slasher
  /// @param slasher The slasher
  /// @return The slash capacity
  function getSlashCapacity(address slasher) external view returns (uint256);

  /// @notice Slashes stakers and rewards the alerter.  Moves slashed staker
  /// funds into the alerter reward funds.  The alerter is then
  /// rewarded by the funds in the alerter reward funds.
  /// @param stakers The list of stakers to slash
  /// @param alerter The alerter that successfully raised the alert
  /// @param principalAmount The amount of the staker's staked LINK amount to slash
  /// @param alerterRewardAmount The reward amount to be given to the alerter
  function slashAndReward(
    address[] calldata stakers,
    address alerter,
    uint256 principalAmount,
    uint256 alerterRewardAmount
  ) external;
}

File 22 of 34 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 23 of 34 : IAccessControlDefaultAdminRules.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/IAccessControlDefaultAdminRules.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";

/**
 * @dev External interface of AccessControlDefaultAdminRules declared to support ERC165 detection.
 *
 * _Available since v4.9._
 */
interface IAccessControlDefaultAdminRules is IAccessControl {
    /**
     * @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next
     * address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`
     * passes.
     */
    event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule);

    /**
     * @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.
     */
    event DefaultAdminTransferCanceled();

    /**
     * @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next
     * delay to be applied between default admin transfer after `effectSchedule` has passed.
     */
    event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule);

    /**
     * @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.
     */
    event DefaultAdminDelayChangeCanceled();

    /**
     * @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.
     */
    function defaultAdmin() external view returns (address);

    /**
     * @dev Returns a tuple of a `newAdmin` and an accept schedule.
     *
     * After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role
     * by calling {acceptDefaultAdminTransfer}, completing the role transfer.
     *
     * A zero value only in `acceptSchedule` indicates no pending admin transfer.
     *
     * NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.
     */
    function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule);

    /**
     * @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started.
     *
     * This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set
     * the acceptance schedule.
     *
     * NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this
     * function returns the new delay. See {changeDefaultAdminDelay}.
     */
    function defaultAdminDelay() external view returns (uint48);

    /**
     * @dev Returns a tuple of `newDelay` and an effect schedule.
     *
     * After the `schedule` passes, the `newDelay` will get into effect immediately for every
     * new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}.
     *
     * A zero value only in `effectSchedule` indicates no pending delay change.
     *
     * NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay}
     * will be zero after the effect schedule.
     */
    function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule);

    /**
     * @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance
     * after the current timestamp plus a {defaultAdminDelay}.
     *
     * Requirements:
     *
     * - Only can be called by the current {defaultAdmin}.
     *
     * Emits a DefaultAdminRoleChangeStarted event.
     */
    function beginDefaultAdminTransfer(address newAdmin) external;

    /**
     * @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
     *
     * A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function.
     *
     * Requirements:
     *
     * - Only can be called by the current {defaultAdmin}.
     *
     * May emit a DefaultAdminTransferCanceled event.
     */
    function cancelDefaultAdminTransfer() external;

    /**
     * @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
     *
     * After calling the function:
     *
     * - `DEFAULT_ADMIN_ROLE` should be granted to the caller.
     * - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder.
     * - {pendingDefaultAdmin} should be reset to zero values.
     *
     * Requirements:
     *
     * - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`.
     * - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.
     */
    function acceptDefaultAdminTransfer() external;

    /**
     * @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting
     * into effect after the current timestamp plus a {defaultAdminDelay}.
     *
     * This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this
     * method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay}
     * set before calling.
     *
     * The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then
     * calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin}
     * complete transfer (including acceptance).
     *
     * The schedule is designed for two scenarios:
     *
     * - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by
     * {defaultAdminDelayIncreaseWait}.
     * - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`.
     *
     * A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change.
     *
     * Requirements:
     *
     * - Only can be called by the current {defaultAdmin}.
     *
     * Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.
     */
    function changeDefaultAdminDelay(uint48 newDelay) external;

    /**
     * @dev Cancels a scheduled {defaultAdminDelay} change.
     *
     * Requirements:
     *
     * - Only can be called by the current {defaultAdmin}.
     *
     * May emit a DefaultAdminDelayChangeCanceled event.
     */
    function rollbackDefaultAdminDelay() external;

    /**
     * @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay})
     * to take effect. Default to 5 days.
     *
     * When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with
     * the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds)
     * that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can
     * be overrode for a custom {defaultAdminDelay} increase scheduling.
     *
     * IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise,
     * there's a risk of setting a high new delay that goes into effect almost immediately without the
     * possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).
     */
    function defaultAdminDelayIncreaseWait() external view returns (uint48);
}

File 24 of 34 : IERC5313.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5313.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface for the Light Contract Ownership Standard.
 *
 * A standardized minimal interface required to identify an account that controls a contract
 *
 * _Available since v4.9._
 */
interface IERC5313 {
    /**
     * @dev Gets the address of the owner.
     */
    function owner() external view returns (address);
}

File 25 of 34 : 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 26 of 34 : ERC677ReceiverInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

interface ERC677ReceiverInterface {
  function onTokenTransfer(
    address sender,
    uint256 amount,
    bytes calldata data
  ) external;
}

File 27 of 34 : 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 28 of 34 : IMigratable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IMigratable {
  /// @notice This error is thrown when the owner tries to set the migration target to the
  /// zero address or an invalid address as well as when the migration target is not set and owner
  /// tries to migrate the contract.
  error InvalidMigrationTarget();

  /// @notice This event is emitted when the migration target is set
  /// @param oldMigrationTarget The previous migration target
  /// @param newMigrationTarget The updated migration target
  event MigrationTargetSet(address indexed oldMigrationTarget, address indexed newMigrationTarget);

  /// @notice Sets the address this contract will be upgraded to
  /// @param newMigrationTarget The address of the migration target
  function setMigrationTarget(address newMigrationTarget) external;

  /// @notice Returns the current migration target of the contract
  /// @return address The current migration target
  function getMigrationTarget() external view returns (address);

  /// @notice Migrates the contract
  /// @param data Optional calldata to call on new contract
  function migrate(bytes calldata data) external;
}

File 29 of 34 : IStakingOwner.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IStakingOwner {
  /// @notice This event is emitted when the staking pool is opened for staking
  event PoolOpened();
  /// @notice This event is emitted when the staking pool is closed
  event PoolClosed();

  /// @notice This error is thrown when an invalid min operator stake amount is
  /// supplied
  error InvalidMinStakeAmount();
  /// @notice This error is raised when attempting to decrease maximum pool size
  /// @param maxPoolSize the proposed maximum pool size
  error InvalidPoolSize(uint256 maxPoolSize);
  /// @notice This error is raised when attempting to decrease maximum stake amount
  /// for the pool members
  /// @param maxStakeAmount the proposed maximum stake amount
  error InvalidMaxStakeAmount(uint256 maxStakeAmount);

  /// @notice This error is thrown when the staking pool is closed.
  error PoolNotOpen();

  /// @notice This error is thrown when the staking pool is open.
  error PoolNotClosed();

  /// @notice This error is thrown when the staking pool has been opened and contract manager tries
  /// to re-open.
  error PoolHasBeenOpened();

  /// @notice This error is thrown when the pool has been closed and contract manager tries to
  /// re-open
  error PoolHasBeenClosed();

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

  /// @notice Opens the pool for staking
  function open() external;

  /// @notice Closes the pool
  function close() external;

  /// @notice Sets the migration proxy contract address
  /// @param migrationProxy The migration proxy contract address
  function setMigrationProxy(address migrationProxy) external;
}

File 30 of 34 : Migratable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {AccessControlDefaultAdminRules} from
  "@openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol";

import {IMigratable} from "./interfaces/IMigratable.sol";

/// @notice Base contract that adds migration functionality.
abstract contract Migratable is IMigratable, AccessControlDefaultAdminRules {
  /// @notice The address of the new contract that this contract will be upgraded to.
  address internal s_migrationTarget;

  function setMigrationTarget(address newMigrationTarget)
    external
    override
    onlyRole(DEFAULT_ADMIN_ROLE)
  {
    _validateMigrationTarget(newMigrationTarget);

    address oldMigrationTarget = s_migrationTarget;
    s_migrationTarget = newMigrationTarget;

    emit MigrationTargetSet(oldMigrationTarget, newMigrationTarget);
  }

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

  /// @notice Helper function for validating the migration target
  /// @param newMigrationTarget The address of the new migration target
  function _validateMigrationTarget(address newMigrationTarget) internal virtual {
    if (
      newMigrationTarget == address(0) || newMigrationTarget == address(this)
        || newMigrationTarget == s_migrationTarget || newMigrationTarget.code.length == 0
    ) {
      revert InvalidMigrationTarget();
    }
  }

  /// @dev Reverts if the migration target is not set
  modifier validateMigrationTargetSet() {
    if (s_migrationTarget == address(0)) {
      revert InvalidMigrationTarget();
    }
    _;
  }
}

File 31 of 34 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 32 of 34 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 33 of 34 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 34 of 34 : 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);
}

File 35 of 34 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

Settings
{
  "remappings": [
    "@chainlink/=lib/chainlink/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@solmate/=lib/solmate/src/",
    "chainlink/=lib/chainlink/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/",
    "lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/src/",
    "lib/openzeppelin-contracts:ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "lib/openzeppelin-contracts:erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "lib/openzeppelin-contracts:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/",
    "lib/openzeppelin-contracts:openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "lib/solmate:ds-test/=lib/solmate/lib/ds-test/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"contract LinkTokenInterface","name":"linkToken","type":"address"},{"internalType":"contract CommunityStakingPool","name":"communityStakingPool","type":"address"},{"internalType":"contract OperatorStakingPool","name":"operatorStakingPool","type":"address"},{"internalType":"uint32","name":"delegationRate","type":"uint32"},{"internalType":"uint32","name":"multiplierDuration","type":"uint32"},{"internalType":"uint48","name":"adminRoleTransferDelay","type":"uint48"}],"internalType":"struct RewardVault.ConstructorParams","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessForbidden","type":"error"},{"inputs":[],"name":"InsufficentRewardsForDelegationRate","type":"error"},{"inputs":[],"name":"InvalidDelegationRate","type":"error"},{"inputs":[],"name":"InvalidEmissionRate","type":"error"},{"inputs":[],"name":"InvalidPool","type":"error"},{"inputs":[],"name":"InvalidRewardAmount","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"NoRewardToClaim","type":"error"},{"inputs":[],"name":"RewardDurationTooShort","type":"error"},{"inputs":[],"name":"VaultAlreadyClosed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"baseRewardPerToken","type":"uint256"}],"name":"CommunityPoolRewardUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"DefaultAdminDelayChangeCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"newDelay","type":"uint48"},{"indexed":false,"internalType":"uint48","name":"effectSchedule","type":"uint48"}],"name":"DefaultAdminDelayChangeScheduled","type":"event"},{"anonymous":false,"inputs":[],"name":"DefaultAdminTransferCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"},{"indexed":false,"internalType":"uint48","name":"acceptSchedule","type":"uint48"}],"name":"DefaultAdminTransferScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldDelegationRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDelegationRate","type":"uint256"}],"name":"DelegationRateSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"vestedReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vestedRewardPerToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reclaimedReward","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isOperatorReward","type":"bool"}],"name":"ForfeitedRewardDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"baseRewardPerToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegatedRewardPerToken","type":"uint256"}],"name":"OperatorPoolRewardUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"emissionRate","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimedRewards","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"bool","name":"shouldForfeit","type":"bool"}],"name":"RewardFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"vestedBaseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vestedDelegatedReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseRewardPerToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"operatorDelegatedRewardPerToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimedBaseRewardsInPeriod","type":"uint256"}],"name":"StakerRewardUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalUnvestedRewards","type":"uint256"}],"name":"VaultClosed","type":"event"},{"anonymous":false,"inputs":[],"name":"VaultOpened","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"emissionRate","type":"uint256"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"beginDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"calculateLatestStakerReward","outputs":[{"components":[{"internalType":"uint112","name":"vestedBaseReward","type":"uint112"},{"internalType":"uint112","name":"vestedDelegatedReward","type":"uint112"},{"internalType":"uint112","name":"baseRewardPerToken","type":"uint112"},{"internalType":"uint112","name":"operatorDelegatedRewardPerToken","type":"uint112"},{"internalType":"enum IRewardVault.StakerType","name":"stakerType","type":"uint8"},{"internalType":"uint112","name":"claimedBaseRewardsInPeriod","type":"uint112"},{"internalType":"uint112","name":"unvestedBaseReward","type":"uint112"}],"internalType":"struct IRewardVault.StakerReward","name":"","type":"tuple"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"}],"name":"changeDefaultAdminDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"close","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"oldPrincipal","type":"uint256"},{"internalType":"uint256","name":"stakedAt","type":"uint256"},{"internalType":"uint256","name":"unstakedAmount","type":"uint256"},{"internalType":"bool","name":"shouldForfeit","type":"bool"}],"name":"concludeRewardPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAdminDelay","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAdminDelayIncreaseWait","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyUnpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDelegationRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFinalVestingCheckpointData","outputs":[{"components":[{"internalType":"uint256","name":"operatorPoolTotalPrincipal","type":"uint256"},{"internalType":"uint256","name":"communityPoolTotalPrincipal","type":"uint256"},{"internalType":"uint256","name":"finalBlockNumber","type":"uint256"}],"internalType":"struct RewardVault.VestingCheckpointData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMultiplierDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardBuckets","outputs":[{"components":[{"components":[{"internalType":"uint80","name":"emissionRate","type":"uint80"},{"internalType":"uint80","name":"rewardDurationEndsAt","type":"uint80"},{"internalType":"uint80","name":"vestedRewardPerToken","type":"uint80"}],"internalType":"struct RewardVault.RewardBucket","name":"operatorBase","type":"tuple"},{"components":[{"internalType":"uint80","name":"emissionRate","type":"uint80"},{"internalType":"uint80","name":"rewardDurationEndsAt","type":"uint80"},{"internalType":"uint80","name":"vestedRewardPerToken","type":"uint80"}],"internalType":"struct RewardVault.RewardBucket","name":"communityBase","type":"tuple"},{"components":[{"internalType":"uint80","name":"emissionRate","type":"uint80"},{"internalType":"uint80","name":"rewardDurationEndsAt","type":"uint80"},{"internalType":"uint80","name":"vestedRewardPerToken","type":"uint80"}],"internalType":"struct RewardVault.RewardBucket","name":"operatorDelegated","type":"tuple"}],"internalType":"struct RewardVault.RewardBuckets","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardPerTokenUpdatedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakingPools","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getStoredReward","outputs":[{"components":[{"internalType":"uint112","name":"vestedBaseReward","type":"uint112"},{"internalType":"uint112","name":"vestedDelegatedReward","type":"uint112"},{"internalType":"uint112","name":"baseRewardPerToken","type":"uint112"},{"internalType":"uint112","name":"operatorDelegatedRewardPerToken","type":"uint112"},{"internalType":"enum IRewardVault.StakerType","name":"stakerType","type":"uint8"},{"internalType":"uint112","name":"claimedBaseRewardsInPeriod","type":"uint112"},{"internalType":"uint112","name":"unvestedBaseReward","type":"uint112"}],"internalType":"struct IRewardVault.StakerReward","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnvestedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"hasRewardAdded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stakingPool","type":"address"}],"name":"hasRewardDurationEnded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOpen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":[],"name":"pendingDefaultAdmin","outputs":[{"internalType":"address","name":"newAdmin","type":"address"},{"internalType":"uint48","name":"schedule","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingDefaultAdminDelay","outputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"},{"internalType":"uint48","name":"schedule","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollbackDefaultAdminDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newDelegationRate","type":"uint256"}],"name":"setDelegationRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"stakerPrincipal","type":"uint256"}],"name":"updateReward","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101006040523480156200001257600080fd5b5060405162005108380380620051088339810160408190526200003591620003ce565b60a08101516000805460ff19169055338181806200009a5760405162461bcd60e51b815260206004820152601e60248201527f416363657373436f6e74726f6c3a20302064656661756c742061646d696e000060448201526064015b60405180910390fd5b600280546001600160d01b0316600160d01b65ffffffffffff851602179055620000c660008262000249565b505082516001600160a01b03169150620000f590505760405163f6b2911f60e01b815260040160405180910390fd5b60208101516001600160a01b0316620001215760405163f6b2911f60e01b815260040160405180910390fd5b60408101516001600160a01b03166200014d5760405163f6b2911f60e01b815260040160405180910390fd5b612710816060015163ffffffff1611156200017b5760405163027953ef60e61b815260040160405180910390fd5b60808181015163ffffffff90811690915281516001600160a01b0390811660a052602080840151821660c05260408085015190921660e05260608401516007805463ffffffff191691909416908117909355815160008152908101929092527f03402a75be6462f03cfaa1a1d189e17ecf71c8b57bf127e93f5f5aeae974b43c910160405180910390a16007805460ff60201b19166401000000001790556040517f539851b0675b473b0130d32676207eae7491ebd4ed2d8aba5c394b6c5c4c2c8190600090a15062000488565b81620002ed576000620002646003546001600160a01b031690565b6001600160a01b031614620002d15760405162461bcd60e51b815260206004820152602c60248201527f416363657373436f6e74726f6c3a2064656661756c742061646d696e20616c7260448201526b1958591e4819dc985b9d195960a21b606482015260840162000091565b600380546001600160a01b0319166001600160a01b0383161790555b620002f98282620002fd565b5050565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff16620002f95760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b80516001600160a01b03811681146200039d57600080fd5b919050565b805163ffffffff811681146200039d57600080fd5b805165ffffffffffff811681146200039d57600080fd5b600060c08284031215620003e157600080fd5b60405160c081016001600160401b03811182821017156200041257634e487b7160e01b600052604160045260246000fd5b604052620004208362000385565b8152620004306020840162000385565b6020820152620004436040840162000385565b60408201526200045660608401620003a2565b60608201526200046960808401620003a2565b60808201526200047c60a08401620003b7565b60a08201529392505050565b60805160a05160c05160e051614b2e620005da600039600081816106b40152818161074201528181610b4301528181610dd001528181610f97015281816111c0015281816112d30152818161136a015281816116ba0152818161183b015281816118bb015281816119be015281816128820152818161291401528181612bf101528181612ecf01528181613712015281816139e801528181613b3901528181613c3f01528181613cd701526141b00152600081816106e801528181610b9701528181610d920152818161100b0152818161119a015281816112ad01528181611344015281816116940152818161186f0152818161199801528181612bcb01528181612df4015281816136e501528181613a7301528181613b7901528181613bff0152613d1b015260008181610c5501528181610e78015261155a0152600081816104720152612d1b0152614b2e6000f3fe608060405234801561001057600080fd5b506004361061027f5760003560e01c806370a205b81161015c578063c00007b0116100ce578063d602b9fd11610087578063d602b9fd146105df578063de0b985a146105e7578063e19e3d7f146105fc578063e63ab1e91461060f578063fcb6813e14610636578063ffafc0b11461063e57600080fd5b8063c00007b014610568578063cc8463c81461057b578063cefc142914610583578063cf3bf2eb1461058b578063cf6eefb71461059e578063d547741f146105cc57600080fd5b806391d148541161012057806391d148541461050b578063a1eda53c1461051e578063a217fddf14610545578063a9d637e11461054d578063b187bd2614610402578063b88a802f1461056057600080fd5b806370a205b81461047057806384ef8ffc146104965780638580cf76146104bb57806387621cbb146104e25780638da5cb5b1461050357600080fd5b806343d726d6116101f55780635350e731116101b95780635350e731146103ef5780635c975abb146104025780635e2746541461040d578063634e93da1461042d578063649a5ec71461044057806365118b3f1461045357600080fd5b806343d726d6146103b257806344ea559a146103ba57806347535d7b146103cd5780634a4e3bd5146103df57806351858e27146103e757600080fd5b80631a399125116102475780631a3991251461031d578063248a9ca3146103305780632f2ff15d1461035457806336568abe146103675780633c70a5841461037a578063417243c91461039d57600080fd5b806301ffc9a714610284578063022d63fb146102ac5780630aa6220b146102c85780630c7e5c23146102d2578063181f5a77146102ea575b600080fd5b61029761029236600461455c565b610668565b60405190151581526020015b60405180910390f35b620697805b60405165ffffffffffff90911681526020016102a3565b6102d0610693565b005b60075463ffffffff165b6040519081526020016102a3565b604080518082018252601181527005265776172645661756c7420312e302e3607c1b602082015290516102a391906145aa565b6102d061032b3660046145f9565b6106a9565b6102dc61033e366004614623565b6000908152600160208190526040909120015490565b6102d061036236600461463c565b6108ce565b6102d061037536600461463c565b61094d565b610382610a37565b604080519384526020840192909252908201526060016102a3565b6103a5610b1c565b6040516102a39190614668565b6102d0610be6565b6102d06103c83660046146b5565b610d04565b600754600160201b900460ff16610297565b6102d0610f2f565b6102d0610f61565b6102976103fd3660046146e8565b610f93565b60005460ff16610297565b61042061041b3660046146e8565b611076565b6040516102a391906147ab565b6102d061043b3660046146e8565b61113d565b6102d061044e3660046147b9565b611151565b61045b611165565b604080519283526020830191909152016102a3565b7f00000000000000000000000000000000000000000000000000000000000000006102dc565b6003546001600160a01b03165b6040516001600160a01b0390911681526020016102a3565b6102dc7fbeec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f681565b6104f56104f03660046146e8565b61117b565b6040516102a39291906147e1565b6104a3611207565b61029761051936600461463c565b611220565b61052661124b565b6040805165ffffffffffff9384168152929091166020830152016102a3565b6102dc600081565b6102dc61055b3660046146e8565b61129b565b6102dc61130e565b6102dc6105763660046146e8565b61167d565b6102b1611753565b6102d06117b2565b6102d061059936600461480b565b611830565b6105a6611bd1565b604080516001600160a01b03909316835265ffffffffffff9091166020830152016102a3565b6102d06105da36600461463c565b611bf2565b6102d0611c6f565b6105ef611c82565b6040516102a3919061485f565b6102d061060a366004614623565b611d88565b6102dc7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610297612055565b610646612093565b60408051825181526020808401519082015291810151908201526060016102a3565b60006001600160e01b031982166318a4c3c360e11b148061068d575061068d826120dc565b92915050565b600061069e81612111565b6106a661211b565b50565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480159061070b5750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b1561072957604051631decfebb60e31b815260040160405180910390fd5b610731612128565b600061076983336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148461227d565b6001600160a01b0384166000908152600c602090815260409182902083518154928501516001600160701b039182166001600160e01b031994851617600160701b918316820217835593850151600183018054606088015192841695169490941791169093029290921780825560808401519394508493919060ff60e01b1916600160e01b83600281111561080057610800614703565b021790555060a0828101516002909201805460c0909401516001600160701b03908116600160701b026001600160e01b03199095169316929092179290921790558151602083015160408085015160608601519486015191516001600160a01b038916957f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a956108c19590949093926001600160701b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b60405180910390a2505050565b8161093f5760405162461bcd60e51b815260206004820152603660248201527f416363657373436f6e74726f6c3a2063616e2774206469726563746c79206772604482015275616e742064656661756c742061646d696e20726f6c6560501b60648201526084015b60405180910390fd5b610949828261245a565b5050565b8115801561096857506003546001600160a01b038281169116145b15610a2d57600080610978611bd1565b90925090506001600160a01b03821615801561099b575065ffffffffffff811615155b80156109ae57504265ffffffffffff8216105b610a185760405162461bcd60e51b815260206004820152603560248201527f416363657373436f6e74726f6c3a206f6e6c792063616e2072656e6f756e636560448201527420696e2074776f2064656c6179656420737465707360581b6064820152608401610936565b50506002805465ffffffffffff60a01b191690555b6109498282612485565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090819081908190610a81906124ff565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150600090610ac8906124ff565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150600090610b0f906124ff565b9296919550919350915050565b604080516002808252606080830184529260009291906020830190803683370190505090507f000000000000000000000000000000000000000000000000000000000000000081600081518110610b7557610b756148f1565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600181518110610bc957610bc96148f1565b6001600160a01b0390921660209283029190910190910152919050565b6000610bf181612111565b600754600160201b900460ff16610c1b5760405163fc5c1c4560e01b815260040160405180910390fd5b6000610c25612551565b50506007805464ff00000000191690555060405163a9059cbb60e01b8152336004820152602481018290529092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316915063a9059cbb906044016020604051808303816000875af1158015610ca7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccb9190614907565b506040518181527f4ef41257ba66fe15e89bff4221410f2c2a555d82f884ca0b3b29898d28635a8f906020015b60405180910390a15050565b610d2e7fbeec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f633611220565b610d4b57604051631decfebb60e31b815260040160405180910390fd5b600754600160201b900460ff16610d755760405163fc5c1c4560e01b815260040160405180910390fd5b610d7d6125f1565b6001600160a01b03831615801590610dc757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614155b8015610e0557507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614155b15610e225760405162820f3560e61b815260040160405180910390fd5b80600003610e435760405163041c928b60e11b815260040160405180910390fd5b610e4b612128565b610e56838383612637565b6040516323b872dd60e01b8152336004820152306024820152604481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610ec9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eed9190614907565b5060408051838152602081018390526001600160a01b038516917f6a6f77044107a33658235d41bedbbaf2fe9ccdceb313143c947a5e76e1ec847491016108c1565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f5981612111565b6106a66126bd565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f8b81612111565b6106a661270f565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036110095760045442600160501b9091046001600160501b03161180159061068d57505060065442600160501b9091046001600160501b03161115919050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361105e57505060055442600160501b9091046001600160501b0316111590565b60405162820f3560e61b815260040160405180910390fd5b61107e61451f565b6001600160a01b0382166000908152600c6020908152604091829020825160e08101845281546001600160701b038082168352600160701b918290048116948301949094526001830154808516958301959095528404909216606083015290916080830190600160e01b900460ff1660028111156110fe576110fe614703565b600281111561110f5761110f614703565b8152600291909101546001600160701b038082166020840152600160701b9091041660409091015292915050565b600061114881612111565b6109498261274c565b600061115c81612111565b610949826127bf565b600080611173600b54612828565b915091509091565b61118361451f565b60008061118f84612860565b90506000816111be577f00000000000000000000000000000000000000000000000000000000000000006111e0565b7f00000000000000000000000000000000000000000000000000000000000000005b905060006111ee868361297f565b90506111fb868285612a7a565b94509450505050915091565b600061121b6003546001600160a01b031690565b905090565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600354600090600160d01b900465ffffffffffff16801515801561127757504265ffffffffffff821610155b61128357600080611173565b600354600160a01b900465ffffffffffff1692909150565b6000806112a783612860565b6112d1577f00000000000000000000000000000000000000000000000000000000000000006112f3565b7f00000000000000000000000000000000000000000000000000000000000000005b90506113076113028483612c6b565b612ce7565b9392505050565b60006113186125f1565b600061132333612860565b905061133b81611334576001612d77565b6002612d77565b600081611368577f000000000000000000000000000000000000000000000000000000000000000061138a565b7f00000000000000000000000000000000000000000000000000000000000000005b90506000611398338361297f565b905060006113a733858461227d565b905060006113c1826113bc6113023388612c6b565b613035565b9050808260c0018181516113d5919061493a565b6001600160701b031690525060a0820180518291906113f590839061495a565b6001600160701b0316905250815160009061141190839061495a565b600084526001600160701b03169050851561144857602083015161143e906001600160701b03168261497a565b6000602085015290505b8060000361146957604051635aa9184d60e01b815260040160405180910390fd5b336000908152600c602090815260409182902085518154928701516001600160701b039182166001600160e01b031994851617600160701b91831682021783559387015160018301805460608a01519284169516949094179116909302929092178082556080860151869392909160ff60e01b1916600160e01b8360028111156114f5576114f5614703565b021790555060a0820151600291909101805460c0909301516001600160701b03908116600160701b026001600160e01b031990941692169190911791909117905560405163a9059cbb60e01b8152336004820152602481018290526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156115a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115c79190614907565b5060405181815233907f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f72419060200160405180910390a2604080840151606085015160a0860151925133937f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a9361166b9360009384939094855260208501939093526001600160701b0391821660408501528116606084015216608082015260a00190565b60405180910390a29550505050505090565b60008061168983612860565b90506000816116b8577f00000000000000000000000000000000000000000000000000000000000000006116da565b7f00000000000000000000000000000000000000000000000000000000000000005b905060006116e8858361297f565b90506000806116f8878487612a7a565b91509150600061171b828561170c88613070565b611716919061498d565b6130ef565b925050508083602001518460000151611734919061495a565b6001600160701b0316611747919061497a565b98975050505050505050565b600354600090600160d01b900465ffffffffffff16801515801561177e57504265ffffffffffff8216105b61179957600254600160d01b900465ffffffffffff166117ac565b600354600160a01b900465ffffffffffff165b91505090565b60006117bc611bd1565b509050336001600160a01b038216146118285760405162461bcd60e51b815260206004820152602860248201527f416363657373436f6e74726f6c3a2070656e64696e672061646d696e206d75736044820152671d081858d8d95c1d60c21b6064820152608401610936565b6106a661313a565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015906118925750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b156118b057604051631decfebb60e31b815260040160405180910390fd5b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146118eb81611334576001612d77565b60006118f887838861227d565b90506000611909826113bc88612ce7565b9050808260c00181815161191d919061493a565b6001600160701b031690525081518190839061193a90839061495a565b6001600160701b0316905250600060a0830152836119655761195d888386613205565b505050611bca565b60c08201516001600160701b03811660000361198f57611986898487613205565b50505050611bca565b6000846119bc577f00000000000000000000000000000000000000000000000000000000000000006119de565b7f00000000000000000000000000000000000000000000000000000000000000005b90506000896119ec83613070565b6119f6919061498d565b905080600003611a8757600060c0860152845183908690611a1890839061495a565b6001600160701b0390811690915260408051600080825260208201529186169082015287151560608201527feb34a4cfa3869c57492c653f9c06f128abac61952688567d4396e9ffaf93a67a915060800160405180910390a1611a7c8b8689613205565b505050505050611bca565b60008a15611ab2578a611aa38a6001600160701b0387166149a0565b611aad91906149cd565b611abd565b836001600160701b03165b9050600087611acd576005611ad0565b60045b90506000611ade83856133a3565b9050611ae9816133b8565b82548390601490611b0b908490600160a01b90046001600160501b03166149e1565b92506101000a8154816001600160501b0302191690836001600160501b031602179055507feb34a4cfa3869c57492c653f9c06f128abac61952688567d4396e9ffaf93a67a838260008c604051611b7d9493929190938452602084019290925260408301521515606082015260800190565b60405180910390a1611b8f888a613424565b611b9883613473565b8860c001818151611ba9919061493a565b6001600160701b0316905250611bc08e898c613205565b5050505050505050505b5050505050565b6002546001600160a01b03811691600160a01b90910465ffffffffffff1690565b81611c655760405162461bcd60e51b815260206004820152603760248201527f416363657373436f6e74726f6c3a2063616e2774206469726563746c7920726560448201527f766f6b652064656661756c742061646d696e20726f6c650000000000000000006064820152608401610936565b61094982826134dc565b6000611c7a81612111565b6106a6613502565b611ced6040805160c081019091526000606082018181526080830182905260a083019190915281908152604080516060810182526000808252602082810182905292820152910190815260408051606081018252600080825260208281018290529282015291015290565b506040805160c0810182526004546001600160501b038082166060808501918252600160501b80850484166080870152600160a01b94859004841660a08701529185528551808201875260055480851682528381048516602083810191909152908690048516828901528087019190915286519182018752600654808516835292830484169082015292900416818401529181019190915290565b6000611d9381612111565b60075463ffffffff1682811480611dab575061271083115b15611dc95760405163027953ef60e61b815260040160405180910390fd5b600654600554600091611de8916001600160501b0391821691166149e1565b6001600160501b0316905060008415611e1757612710611e0886846149a0565b611e1291906149cd565b611e1a565b60005b905080158015611e2957508415155b8015611e3457508115155b15611e525760405163ba00abbd60e01b815260040160405180910390fd5b611e5a612128565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090611e9e906124ff565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152611edf906124ff565b611ee9919061497a565b90506000611ef7838561498d565b9050611f02816133b8565b6005805469ffffffffffffffffffff19166001600160501b0392909216919091179055611f2e836133b8565b6006805469ffffffffffffffffffff19166001600160501b03929092169190911790556000879003611f7e5760068054600160501b600160a01b0319169055611f796005838361350d565b611ff0565b6127108703611fa65760058054600160501b600160a01b0319169055611f796006838561350d565b8115611ff0576000612710611fbb89856149a0565b611fc591906149cd565b90506000611fd3828561498d565b9050611fe16005828561350d565b611fed6006838761350d565b50505b611ff98761355c565b6007805463ffffffff191663ffffffff9290921691909117905560408051868152602081018990527f03402a75be6462f03cfaa1a1d189e17ecf71c8b57bf127e93f5f5aeae974b43c910160405180910390a150505050505050565b6004546000906001600160501b031615158061207b57506005546001600160501b031615155b8061121b5750506006546001600160501b0316151590565b6120b760405180606001604052806000815260200160008152602001600081525090565b506040805160608101825260085481526009546020820152600a549181019190915290565b60006001600160e01b03198216637965db0b60e01b148061068d57506301ffc9a760e01b6001600160e01b031983161461068d565b6106a681336135c1565b61212660008061361a565b565b600080612136600b54612828565b91509150428214801561214857504281145b15612151575050565b600080600061215e6136da565b92509250925061216d836133b8565b600580546001600160501b0392909216600160a01b02600160a01b600160f01b03199092169190911790556121a1826133b8565b600480546001600160501b0392909216600160a01b02600160a01b600160f01b03199092169190911790556121d5816133b8565b600680546001600160501b0392909216600160a01b02600160a01b600160f01b031990921691909117905561220a4280613823565b6040518381527f1236d5f7290187e9ac80880b3e28a193a9f38bb8bdc8a8a197744d80c50ca3269060200160405180910390a160408051838152602081018390527f9eecdebdae54d18e0c665dae22811251367c65647ec09aa5e33f001a5c147d22910160405180910390a15050505050565b61228561451f565b6001600160a01b0384166000908152600c60209081526040808320815160e08101835281546001600160701b038082168352600160701b9182900481169583019590955260018301548086169483019490945283049093166060840152906080830190600160e01b900460ff16600281111561230357612303614703565b600281111561231457612314614703565b8152600291909101546001600160701b038082166020840152600160701b90910416604090910152905060008160800151600281111561235657612356614703565b03612397578361236757600161236a565b60025b8160800190600281111561238057612380614703565b9081600281111561239357612393614703565b9052505b6123e36123de8285876123bc57600554600160a01b90046001600160501b03166123d0565b600454600160a01b90046001600160501b03165b6001600160501b0316613847565b613473565b8160c0018181516123f4919061495a565b6001600160701b031690525083156124485760065461242a906123de9083908690600160a01b90046001600160501b031661386b565b8160200181815161243b919061495a565b6001600160701b03169052505b6124528185613424565b949350505050565b6000828152600160208190526040909120015461247681612111565b6124808383613886565b505050565b6001600160a01b03811633146124f55760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610936565b6109498282613930565b60004282602001516001600160501b03161115612549574282602001516001600160501b031661252f919061498d565b825161254491906001600160501b03166149a0565b61068d565b600092915050565b6000806000806000612561612128565b600061256d600461396b565b9050600061257b600561396b565b90506000612589600661396b565b9050600081612598848661497a565b6125a2919061497a565b90506125ac6139e6565b6006546005546004546001600160501b03928316926125ce92811691166149e1565b6125d891906149e1565b6001600160501b03169990985092965090945092509050565b60005460ff16156121265760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610936565b6007546000906126549085908590859063ffffffff161515613afc565b90508060600151600014612675578051606082015161267591600591613ebc565b60808101511561269657612696600460000182602001518360800151613ebc565b60a0810151156126b7576126b7600460020182604001518360a00151613ebc565b50505050565b6126c5613f6b565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6127176125f1565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586126f23390565b6000612756611753565b61275f42613fb4565b6127699190614a01565b9050612775828261401b565b60405165ffffffffffff821681526001600160a01b038316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b60006127ca8261409a565b6127d342613fb4565b6127dd9190614a01565b90506127e9828261361a565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9101610cf8565b6000808061283e670de0b6b3a7640000856149cd565b90506000612854670de0b6b3a764000086614a20565b91959194509092505050565b6040516336b87bd760e11b81526001600160a01b0382811660048301526000917f000000000000000000000000000000000000000000000000000000000000000090911690636d70f7ae90602401602060405180830381865afa1580156128cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ef9190614907565b8061068d5750604051637d3374e560e01b81526001600160a01b0383811660048301527f00000000000000000000000000000000000000000000000000000000000000001690637d3374e590602401602060405180830381865afa15801561295b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068d9190614907565b600754600090600160201b900460ff16612a0f57600a54604051634c5b725f60e01b81526001600160a01b038581166004830152602482019290925290831690634c5b725f906044015b602060405180830381865afa1580156129e6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a0a9190614a34565b611307565b604051630706983f60e51b81526001600160a01b03848116600483015283169063e0d307e0906024015b602060405180830381865afa158015612a56573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113079190614a34565b612a8261451f565b6001600160a01b0384166000908152600c60209081526040808320815160e08101835281546001600160701b038082168352600160701b91829004811695830195909552600183015480861694830194909452830490931660608401528392916080830190600160e01b900460ff166002811115612b0257612b02614703565b6002811115612b1357612b13614703565b8152600291909101546001600160701b038082166020840152600160701b90910416604090910152905060008080612b496136da565b925092509250612b686123de858a8a612b625786613847565b85613847565b8460c001818151612b79919061495a565b6001600160701b03169052508615612bb757612b996123de858a8461386b565b84602001818151612baa919061495a565b6001600160701b03169052505b6000612c15856113bc6113028d8c612bef577f0000000000000000000000000000000000000000000000000000000000000000612c6b565b7f0000000000000000000000000000000000000000000000000000000000000000612c6b565b90508085600001818151612c29919061495a565b6001600160701b031690525060c0850151600090612c4890839061493a565b600060c0880152959b6001600160701b039096169a509498505050505050505050565b600754600090600160201b900460ff16612cb957600a54604051634b669bff60e11b81526001600160a01b0385811660048301526024820192909252908316906396cd37fe906044016129c9565b604051631624e46160e21b81526001600160a01b038481166004830152831690635893918490602401612a39565b600081600003612cf957506000919050565b600754600160201b900460ff16612d195750670de0b6b3a7640000919050565b7f00000000000000000000000000000000000000000000000000000000000000006000819003612d535750670de0b6b3a764000092915050565b611307612d69612d63854261498d565b836133a3565b670de0b6b3a76400006140d9565b600080612d85600b54612828565b90925090506001836002811115612d9e57612d9e614703565b03612ea357428203612daf57505050565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152612e2390612e1e90612e187f0000000000000000000000000000000000000000000000000000000000000000613070565b856140ef565b6133b8565b600580546001600160501b0392909216600160a01b02600160a01b600160f01b0319909216919091179055612e584282613823565b600554604051600160a01b9091046001600160501b031681527f1236d5f7290187e9ac80880b3e28a193a9f38bb8bdc8a8a197744d80c50ca3269060200160405180910390a1505050565b6002836002811115612eb757612eb7614703565b0361248057428103612ec857505050565b6000612ef37f0000000000000000000000000000000000000000000000000000000000000000613070565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150612f3d90612e1e9083856140ef565b60048054600160a01b600160f01b031916600160a01b6001600160501b03938416810291909117909155604080516060810182526006548085168252600160501b8104851660208301529290920490921691810191909152612fa490612e1e9083856140ef565b600680546001600160501b0392909216600160a01b02600160a01b600160f01b0319909216919091179055612fd98342613823565b600454600654604080516001600160501b03600160a01b94859004811682529390920490921660208201527f9eecdebdae54d18e0c665dae22811251367c65647ec09aa5e33f001a5c147d22910160405180910390a150505050565b60008260a001516130666123de848660a001518760c00151613057919061495a565b6001600160701b031690614197565b611307919061493a565b600754600090600160201b900460ff1661308d57612544826141ac565b816001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130cb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068d9190614a34565b60008060008460000361310a57506000915081905080613133565b6000808086156131285787925061312183886133a3565b915061312b565b50865b919450925090505b9250925092565b600080613145611bd1565b9150915061315a8165ffffffffffff16151590565b801561316d57504265ffffffffffff8216105b6131ca5760405162461bcd60e51b815260206004820152602860248201527f416363657373436f6e74726f6c3a207472616e736665722064656c6179206e6f6044820152671d081c185cdcd95960c21b6064820152608401610936565b6131e660006131e16003546001600160a01b031690565b613930565b6131f1600083613886565b5050600280546001600160d01b0319169055565b6001600160a01b0383166000908152600c602090815260409182902084518154928601516001600160701b039182166001600160e01b031994851617600160701b91831682021783559386015160018301805460608901519284169516949094179116909302929092178082556080850151859392909160ff60e01b1916600160e01b83600281111561329a5761329a614703565b021790555060a08201516002909101805460c0909301516001600160701b03908116600160701b026001600160e01b03199094169216919091179190911790556040516001600160a01b038416907fa6b1f21803a1471cf6981bffc4c07ff6610f2b88efc44f62b96dca65a63d3d279061331990841515815260200190565b60405180910390a2826001600160a01b03167f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a83600001518460200151856040015186606001518760a001516040516108c19594939291906001600160701b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b600061130783670de0b6b3a7640000846141f7565b60006001600160501b038211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201526530206269747360d01b6064820152608401610936565b5090565b8015613456576004546001600160501b03600160a01b9182900481166040850152600654919091041660608301525050565b6005546001600160501b03600160a01b9091041660408301525050565b60006001600160701b038211156134205760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663132206269747360c81b6064820152608401610936565b600082815260016020819052604090912001546134f881612111565b6124808383613930565b61212660008061401b565b8060000361351a57505050565b61353161352782846149cd565b612e1e904261497a565b83546001600160501b0391909116600160501b02600160501b600160a01b0319909116179092555050565b600063ffffffff8211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610936565b6135cb8282611220565b610949576135d881614215565b6135e3836020614227565b6040516020016135f4929190614a4d565b60408051601f198184030181529082905262461bcd60e51b8252610936916004016145aa565b600354600160d01b900465ffffffffffff16801561369d574265ffffffffffff8216101561367357600354600280546001600160d01b0316600160a01b90920465ffffffffffff16600160d01b0291909117905561369d565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec590600090a15b50600380546001600160a01b0316600160a01b65ffffffffffff948516026001600160d01b031617600160d01b9290931691909102919091179055565b6000806000806137097f0000000000000000000000000000000000000000000000000000000000000000613070565b905060006137367f0000000000000000000000000000000000000000000000000000000000000000613070565b9050600080613746600b54612828565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152919350915061378e9085846140ef565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b90910416918101919091526137d19085846140ef565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b90910416918101919091526138149086856140ef565b96509650965050505050909192565b8061383683670de0b6b3a76400006149a0565b613840919061497a565b600b555050565b6000806138628486604001516001600160701b0316856143c3565b95945050505050565b6000806138628486606001516001600160701b0316856143c3565b8161392657600061389f6003546001600160a01b031690565b6001600160a01b03161461390a5760405162461bcd60e51b815260206004820152602c60248201527f416363657373436f6e74726f6c3a2064656661756c742061646d696e20616c7260448201526b1958591e4819dc985b9d195960a21b6064820152608401610936565b600380546001600160a01b0319166001600160a01b0383161790555b61094982826143d9565b8115801561394b57506003546001600160a01b038281169116145b1561396157600380546001600160a01b03191690555b6109498282614444565b6040805160608101825282546001600160501b038082168352600160501b820481166020840152600160a01b909104169181019190915260009081906139b0906124ff565b90506139bb426133b8565b83546001600160501b0391909116600160501b02600160501b600160a01b0319909116179092555090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a689190614a34565b6008600001819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613acf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af39190614a34565b60095543600a55565b613b356040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031603613b77576000613bf9565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630fbc8f5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bf99190614a34565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031603613c3d576000613cbf565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630fbc8f5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cbf9190614a34565b90506000613ccd828461497a565b90506000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168c6001600160a01b031603613d19578a9350899150613db9565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168c6001600160a01b031603613d5c575089915088613db9565b613d678b87876144ab565b613d728a87876144ea565b84613d7d878d6149a0565b613d8791906149cd565b935084613d94878c6149a0565b613d9e91906149cd565b9150613daa848c61498d565b9250613db6828b61498d565b90505b6000808a8015613dc857508815155b15613e855760075461271090613de49063ffffffff16876149a0565b613dee91906149cd565b9150600085118015613dfe575081155b15613e1c57604051633853986560e01b815260040160405180910390fd5b613e26828661498d565b60075490955061271090613e409063ffffffff16856149a0565b613e4a91906149cd565b9050600083118015613e5a575080155b15613e785760405163041c928b60e11b815260040160405180910390fd5b613e82818461498d565b92505b6040805160c0810182529586526020860196909652948401526060830152608082015260a081019190915298975050505050505050565b6040805160608101825284546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090613eff906124ff565b905081613f0c828561497a565b1015613f2a5760405162da056d60e81b815260040160405180910390fd5b613f3e84613f38838661497a565b8461350d565b613f47826133b8565b845469ffffffffffffffffffff19166001600160501b039190911617909355505050565b60005460ff166121265760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610936565b600065ffffffffffff8211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203460448201526538206269747360d01b6064820152608401610936565b6000614025611bd1565b6002805465ffffffffffff8616600160a01b026001600160d01b03199091166001600160a01b03881617179055915061406790508165ffffffffffff16151590565b15612480576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a960510990600090a1505050565b6000806140a5611753565b90508065ffffffffffff168365ffffffffffff16116140c857612a0a8382614ac2565b61130765ffffffffffff8416620697805b60008183106140e85781611307565b5090919050565b60008260000361410d575060408301516001600160501b0316611307565b600061412685602001516001600160501b0316426140d9565b905082811161414457505060408301516001600160501b0316611307565b6000614150848361498d565b90506141758587600001516001600160501b03168361416f91906149a0565b906133a3565b86604001516001600160501b031661418d919061497a565b9695505050505050565b60006113078383670de0b6b3a76400006141f7565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316146141ef5760095461068d565b505060085490565b600082600019048411830215820261420e57600080fd5b5091020490565b606061068d6001600160a01b03831660145b606060006142368360026149a0565b61424190600261497a565b67ffffffffffffffff811115614259576142596148db565b6040519080825280601f01601f191660200182016040528015614283576020820181803683370190505b509050600360fc1b8160008151811061429e5761429e6148f1565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106142cd576142cd6148f1565b60200101906001600160f81b031916908160001a90535060006142f18460026149a0565b6142fc90600161497a565b90505b6001811115614374576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614330576143306148f1565b1a60f81b828281518110614346576143466148f1565b60200101906001600160f81b031916908160001a90535060049490941c9361436d81614ae1565b90506142ff565b5083156113075760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610936565b60006124526143d2848461498d565b8590614197565b6143e38282611220565b6109495760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b61444e8282611220565b156109495760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b82158015906144cc575081158015906144cc5750806144ca83856149a0565b105b1561248057604051633853986560e01b815260040160405180910390fd5b81158015906145015750806144ff83856149a0565b105b156124805760405163041c928b60e11b815260040160405180910390fd5b6040805160e08101825260008082526020820181905291810182905260608101829052906080820190815260006020820181905260409091015290565b60006020828403121561456e57600080fd5b81356001600160e01b03198116811461130757600080fd5b60005b838110156145a1578181015183820152602001614589565b50506000910152565b60208152600082518060208401526145c9816040850160208701614586565b601f01601f19169190910160400192915050565b80356001600160a01b03811681146145f457600080fd5b919050565b6000806040838503121561460c57600080fd5b614615836145dd565b946020939093013593505050565b60006020828403121561463557600080fd5b5035919050565b6000806040838503121561464f57600080fd5b8235915061465f602084016145dd565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156146a95783516001600160a01b031683529284019291840191600101614684565b50909695505050505050565b6000806000606084860312156146ca57600080fd5b6146d3846145dd565b95602085013595506040909401359392505050565b6000602082840312156146fa57600080fd5b611307826145dd565b634e487b7160e01b600052602160045260246000fd5b6001600160701b038082511683528060208301511660208401528060408301511660408401528060608301511660608401525060808101516003811061476f57634e487b7160e01b600052602160045260246000fd5b8060808401525060a081015161479060a08401826001600160701b03169052565b5060c081015161248060c08401826001600160701b03169052565b60e0810161068d8284614719565b6000602082840312156147cb57600080fd5b813565ffffffffffff8116811461130757600080fd5b61010081016147f08285614719565b8260e08301529392505050565b80151581146106a657600080fd5b600080600080600060a0868803121561482357600080fd5b61482c866145dd565b94506020860135935060408601359250606086013591506080860135614851816147fd565b809150509295509295909350565b815180516001600160501b039081168352602080830151821681850152604092830151821683850152808501518051831660608601528082015183166080860152830151821660a0850152828501518051831660c086015290810151821660e085015291820151166101008301526101208201905b5092915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561491957600080fd5b8151611307816147fd565b634e487b7160e01b600052601160045260246000fd5b6001600160701b038281168282160390808211156148d4576148d4614924565b6001600160701b038181168382160190808211156148d4576148d4614924565b8082018082111561068d5761068d614924565b8181038181111561068d5761068d614924565b808202811582820484141761068d5761068d614924565b634e487b7160e01b600052601260045260246000fd5b6000826149dc576149dc6149b7565b500490565b6001600160501b038181168382160190808211156148d4576148d4614924565b65ffffffffffff8181168382160190808211156148d4576148d4614924565b600082614a2f57614a2f6149b7565b500690565b600060208284031215614a4657600080fd5b5051919050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351614a85816017850160208801614586565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351614ab6816028840160208801614586565b01602801949350505050565b65ffffffffffff8281168282160390808211156148d4576148d4614924565b600081614af057614af0614924565b50600019019056fea2646970667358221220759dc3b88a3e08ba309d211f431f2485ef8307884fb25ec991b4b67724d1cfe864736f6c63430008130033000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc50000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061027f5760003560e01c806370a205b81161015c578063c00007b0116100ce578063d602b9fd11610087578063d602b9fd146105df578063de0b985a146105e7578063e19e3d7f146105fc578063e63ab1e91461060f578063fcb6813e14610636578063ffafc0b11461063e57600080fd5b8063c00007b014610568578063cc8463c81461057b578063cefc142914610583578063cf3bf2eb1461058b578063cf6eefb71461059e578063d547741f146105cc57600080fd5b806391d148541161012057806391d148541461050b578063a1eda53c1461051e578063a217fddf14610545578063a9d637e11461054d578063b187bd2614610402578063b88a802f1461056057600080fd5b806370a205b81461047057806384ef8ffc146104965780638580cf76146104bb57806387621cbb146104e25780638da5cb5b1461050357600080fd5b806343d726d6116101f55780635350e731116101b95780635350e731146103ef5780635c975abb146104025780635e2746541461040d578063634e93da1461042d578063649a5ec71461044057806365118b3f1461045357600080fd5b806343d726d6146103b257806344ea559a146103ba57806347535d7b146103cd5780634a4e3bd5146103df57806351858e27146103e757600080fd5b80631a399125116102475780631a3991251461031d578063248a9ca3146103305780632f2ff15d1461035457806336568abe146103675780633c70a5841461037a578063417243c91461039d57600080fd5b806301ffc9a714610284578063022d63fb146102ac5780630aa6220b146102c85780630c7e5c23146102d2578063181f5a77146102ea575b600080fd5b61029761029236600461455c565b610668565b60405190151581526020015b60405180910390f35b620697805b60405165ffffffffffff90911681526020016102a3565b6102d0610693565b005b60075463ffffffff165b6040519081526020016102a3565b604080518082018252601181527005265776172645661756c7420312e302e3607c1b602082015290516102a391906145aa565b6102d061032b3660046145f9565b6106a9565b6102dc61033e366004614623565b6000908152600160208190526040909120015490565b6102d061036236600461463c565b6108ce565b6102d061037536600461463c565b61094d565b610382610a37565b604080519384526020840192909252908201526060016102a3565b6103a5610b1c565b6040516102a39190614668565b6102d0610be6565b6102d06103c83660046146b5565b610d04565b600754600160201b900460ff16610297565b6102d0610f2f565b6102d0610f61565b6102976103fd3660046146e8565b610f93565b60005460ff16610297565b61042061041b3660046146e8565b611076565b6040516102a391906147ab565b6102d061043b3660046146e8565b61113d565b6102d061044e3660046147b9565b611151565b61045b611165565b604080519283526020830191909152016102a3565b7f000000000000000000000000000000000000000000000000000000000076a7006102dc565b6003546001600160a01b03165b6040516001600160a01b0390911681526020016102a3565b6102dc7fbeec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f681565b6104f56104f03660046146e8565b61117b565b6040516102a39291906147e1565b6104a3611207565b61029761051936600461463c565b611220565b61052661124b565b6040805165ffffffffffff9384168152929091166020830152016102a3565b6102dc600081565b6102dc61055b3660046146e8565b61129b565b6102dc61130e565b6102dc6105763660046146e8565b61167d565b6102b1611753565b6102d06117b2565b6102d061059936600461480b565b611830565b6105a6611bd1565b604080516001600160a01b03909316835265ffffffffffff9091166020830152016102a3565b6102d06105da36600461463c565b611bf2565b6102d0611c6f565b6105ef611c82565b6040516102a3919061485f565b6102d061060a366004614623565b611d88565b6102dc7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610297612055565b610646612093565b60408051825181526020808401519082015291810151908201526060016102a3565b60006001600160e01b031982166318a4c3c360e11b148061068d575061068d826120dc565b92915050565b600061069e81612111565b6106a661211b565b50565b336001600160a01b037f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5161480159061070b5750336001600160a01b037f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e1614155b1561072957604051631decfebb60e31b815260040160405180910390fd5b610731612128565b600061076983336001600160a01b037f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc516148461227d565b6001600160a01b0384166000908152600c602090815260409182902083518154928501516001600160701b039182166001600160e01b031994851617600160701b918316820217835593850151600183018054606088015192841695169490941791169093029290921780825560808401519394508493919060ff60e01b1916600160e01b83600281111561080057610800614703565b021790555060a0828101516002909201805460c0909401516001600160701b03908116600160701b026001600160e01b03199095169316929092179290921790558151602083015160408085015160608601519486015191516001600160a01b038916957f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a956108c19590949093926001600160701b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b60405180910390a2505050565b8161093f5760405162461bcd60e51b815260206004820152603660248201527f416363657373436f6e74726f6c3a2063616e2774206469726563746c79206772604482015275616e742064656661756c742061646d696e20726f6c6560501b60648201526084015b60405180910390fd5b610949828261245a565b5050565b8115801561096857506003546001600160a01b038281169116145b15610a2d57600080610978611bd1565b90925090506001600160a01b03821615801561099b575065ffffffffffff811615155b80156109ae57504265ffffffffffff8216105b610a185760405162461bcd60e51b815260206004820152603560248201527f416363657373436f6e74726f6c3a206f6e6c792063616e2072656e6f756e636560448201527420696e2074776f2064656c6179656420737465707360581b6064820152608401610936565b50506002805465ffffffffffff60a01b191690555b6109498282612485565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090819081908190610a81906124ff565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150600090610ac8906124ff565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150600090610b0f906124ff565b9296919550919350915050565b604080516002808252606080830184529260009291906020830190803683370190505090507f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc581600081518110610b7557610b756148f1565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e81600181518110610bc957610bc96148f1565b6001600160a01b0390921660209283029190910190910152919050565b6000610bf181612111565b600754600160201b900460ff16610c1b5760405163fc5c1c4560e01b815260040160405180910390fd5b6000610c25612551565b50506007805464ff00000000191690555060405163a9059cbb60e01b8152336004820152602481018290529092507f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca6001600160a01b0316915063a9059cbb906044016020604051808303816000875af1158015610ca7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccb9190614907565b506040518181527f4ef41257ba66fe15e89bff4221410f2c2a555d82f884ca0b3b29898d28635a8f906020015b60405180910390a15050565b610d2e7fbeec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f633611220565b610d4b57604051631decfebb60e31b815260040160405180910390fd5b600754600160201b900460ff16610d755760405163fc5c1c4560e01b815260040160405180910390fd5b610d7d6125f1565b6001600160a01b03831615801590610dc757507f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b0316836001600160a01b031614155b8015610e0557507f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316836001600160a01b031614155b15610e225760405162820f3560e61b815260040160405180910390fd5b80600003610e435760405163041c928b60e11b815260040160405180910390fd5b610e4b612128565b610e56838383612637565b6040516323b872dd60e01b8152336004820152306024820152604481018390527f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca6001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610ec9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eed9190614907565b5060408051838152602081018390526001600160a01b038516917f6a6f77044107a33658235d41bedbbaf2fe9ccdceb313143c947a5e76e1ec847491016108c1565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f5981612111565b6106a66126bd565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f8b81612111565b6106a661270f565b60007f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316826001600160a01b0316036110095760045442600160501b9091046001600160501b03161180159061068d57505060065442600160501b9091046001600160501b03161115919050565b7f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b0316826001600160a01b03160361105e57505060055442600160501b9091046001600160501b0316111590565b60405162820f3560e61b815260040160405180910390fd5b61107e61451f565b6001600160a01b0382166000908152600c6020908152604091829020825160e08101845281546001600160701b038082168352600160701b918290048116948301949094526001830154808516958301959095528404909216606083015290916080830190600160e01b900460ff1660028111156110fe576110fe614703565b600281111561110f5761110f614703565b8152600291909101546001600160701b038082166020840152600160701b9091041660409091015292915050565b600061114881612111565b6109498261274c565b600061115c81612111565b610949826127bf565b600080611173600b54612828565b915091509091565b61118361451f565b60008061118f84612860565b90506000816111be577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6111e0565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b905060006111ee868361297f565b90506111fb868285612a7a565b94509450505050915091565b600061121b6003546001600160a01b031690565b905090565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600354600090600160d01b900465ffffffffffff16801515801561127757504265ffffffffffff821610155b61128357600080611173565b600354600160a01b900465ffffffffffff1692909150565b6000806112a783612860565b6112d1577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6112f3565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b90506113076113028483612c6b565b612ce7565b9392505050565b60006113186125f1565b600061132333612860565b905061133b81611334576001612d77565b6002612d77565b600081611368577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e61138a565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b90506000611398338361297f565b905060006113a733858461227d565b905060006113c1826113bc6113023388612c6b565b613035565b9050808260c0018181516113d5919061493a565b6001600160701b031690525060a0820180518291906113f590839061495a565b6001600160701b0316905250815160009061141190839061495a565b600084526001600160701b03169050851561144857602083015161143e906001600160701b03168261497a565b6000602085015290505b8060000361146957604051635aa9184d60e01b815260040160405180910390fd5b336000908152600c602090815260409182902085518154928701516001600160701b039182166001600160e01b031994851617600160701b91831682021783559387015160018301805460608a01519284169516949094179116909302929092178082556080860151869392909160ff60e01b1916600160e01b8360028111156114f5576114f5614703565b021790555060a0820151600291909101805460c0909301516001600160701b03908116600160701b026001600160e01b031990941692169190911791909117905560405163a9059cbb60e01b8152336004820152602481018290526001600160a01b037f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca169063a9059cbb906044016020604051808303816000875af11580156115a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115c79190614907565b5060405181815233907f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f72419060200160405180910390a2604080840151606085015160a0860151925133937f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a9361166b9360009384939094855260208501939093526001600160701b0391821660408501528116606084015216608082015260a00190565b60405180910390a29550505050505090565b60008061168983612860565b90506000816116b8577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6116da565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b905060006116e8858361297f565b90506000806116f8878487612a7a565b91509150600061171b828561170c88613070565b611716919061498d565b6130ef565b925050508083602001518460000151611734919061495a565b6001600160701b0316611747919061497a565b98975050505050505050565b600354600090600160d01b900465ffffffffffff16801515801561177e57504265ffffffffffff8216105b61179957600254600160d01b900465ffffffffffff166117ac565b600354600160a01b900465ffffffffffff165b91505090565b60006117bc611bd1565b509050336001600160a01b038216146118285760405162461bcd60e51b815260206004820152602860248201527f416363657373436f6e74726f6c3a2070656e64696e672061646d696e206d75736044820152671d081858d8d95c1d60c21b6064820152608401610936565b6106a661313a565b336001600160a01b037f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc516148015906118925750336001600160a01b037f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e1614155b156118b057604051631decfebb60e31b815260040160405180910390fd5b336001600160a01b037f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc516146118eb81611334576001612d77565b60006118f887838861227d565b90506000611909826113bc88612ce7565b9050808260c00181815161191d919061493a565b6001600160701b031690525081518190839061193a90839061495a565b6001600160701b0316905250600060a0830152836119655761195d888386613205565b505050611bca565b60c08201516001600160701b03811660000361198f57611986898487613205565b50505050611bca565b6000846119bc577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6119de565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b90506000896119ec83613070565b6119f6919061498d565b905080600003611a8757600060c0860152845183908690611a1890839061495a565b6001600160701b0390811690915260408051600080825260208201529186169082015287151560608201527feb34a4cfa3869c57492c653f9c06f128abac61952688567d4396e9ffaf93a67a915060800160405180910390a1611a7c8b8689613205565b505050505050611bca565b60008a15611ab2578a611aa38a6001600160701b0387166149a0565b611aad91906149cd565b611abd565b836001600160701b03165b9050600087611acd576005611ad0565b60045b90506000611ade83856133a3565b9050611ae9816133b8565b82548390601490611b0b908490600160a01b90046001600160501b03166149e1565b92506101000a8154816001600160501b0302191690836001600160501b031602179055507feb34a4cfa3869c57492c653f9c06f128abac61952688567d4396e9ffaf93a67a838260008c604051611b7d9493929190938452602084019290925260408301521515606082015260800190565b60405180910390a1611b8f888a613424565b611b9883613473565b8860c001818151611ba9919061493a565b6001600160701b0316905250611bc08e898c613205565b5050505050505050505b5050505050565b6002546001600160a01b03811691600160a01b90910465ffffffffffff1690565b81611c655760405162461bcd60e51b815260206004820152603760248201527f416363657373436f6e74726f6c3a2063616e2774206469726563746c7920726560448201527f766f6b652064656661756c742061646d696e20726f6c650000000000000000006064820152608401610936565b61094982826134dc565b6000611c7a81612111565b6106a6613502565b611ced6040805160c081019091526000606082018181526080830182905260a083019190915281908152604080516060810182526000808252602082810182905292820152910190815260408051606081018252600080825260208281018290529282015291015290565b506040805160c0810182526004546001600160501b038082166060808501918252600160501b80850484166080870152600160a01b94859004841660a08701529185528551808201875260055480851682528381048516602083810191909152908690048516828901528087019190915286519182018752600654808516835292830484169082015292900416818401529181019190915290565b6000611d9381612111565b60075463ffffffff1682811480611dab575061271083115b15611dc95760405163027953ef60e61b815260040160405180910390fd5b600654600554600091611de8916001600160501b0391821691166149e1565b6001600160501b0316905060008415611e1757612710611e0886846149a0565b611e1291906149cd565b611e1a565b60005b905080158015611e2957508415155b8015611e3457508115155b15611e525760405163ba00abbd60e01b815260040160405180910390fd5b611e5a612128565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090611e9e906124ff565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152611edf906124ff565b611ee9919061497a565b90506000611ef7838561498d565b9050611f02816133b8565b6005805469ffffffffffffffffffff19166001600160501b0392909216919091179055611f2e836133b8565b6006805469ffffffffffffffffffff19166001600160501b03929092169190911790556000879003611f7e5760068054600160501b600160a01b0319169055611f796005838361350d565b611ff0565b6127108703611fa65760058054600160501b600160a01b0319169055611f796006838561350d565b8115611ff0576000612710611fbb89856149a0565b611fc591906149cd565b90506000611fd3828561498d565b9050611fe16005828561350d565b611fed6006838761350d565b50505b611ff98761355c565b6007805463ffffffff191663ffffffff9290921691909117905560408051868152602081018990527f03402a75be6462f03cfaa1a1d189e17ecf71c8b57bf127e93f5f5aeae974b43c910160405180910390a150505050505050565b6004546000906001600160501b031615158061207b57506005546001600160501b031615155b8061121b5750506006546001600160501b0316151590565b6120b760405180606001604052806000815260200160008152602001600081525090565b506040805160608101825260085481526009546020820152600a549181019190915290565b60006001600160e01b03198216637965db0b60e01b148061068d57506301ffc9a760e01b6001600160e01b031983161461068d565b6106a681336135c1565b61212660008061361a565b565b600080612136600b54612828565b91509150428214801561214857504281145b15612151575050565b600080600061215e6136da565b92509250925061216d836133b8565b600580546001600160501b0392909216600160a01b02600160a01b600160f01b03199092169190911790556121a1826133b8565b600480546001600160501b0392909216600160a01b02600160a01b600160f01b03199092169190911790556121d5816133b8565b600680546001600160501b0392909216600160a01b02600160a01b600160f01b031990921691909117905561220a4280613823565b6040518381527f1236d5f7290187e9ac80880b3e28a193a9f38bb8bdc8a8a197744d80c50ca3269060200160405180910390a160408051838152602081018390527f9eecdebdae54d18e0c665dae22811251367c65647ec09aa5e33f001a5c147d22910160405180910390a15050505050565b61228561451f565b6001600160a01b0384166000908152600c60209081526040808320815160e08101835281546001600160701b038082168352600160701b9182900481169583019590955260018301548086169483019490945283049093166060840152906080830190600160e01b900460ff16600281111561230357612303614703565b600281111561231457612314614703565b8152600291909101546001600160701b038082166020840152600160701b90910416604090910152905060008160800151600281111561235657612356614703565b03612397578361236757600161236a565b60025b8160800190600281111561238057612380614703565b9081600281111561239357612393614703565b9052505b6123e36123de8285876123bc57600554600160a01b90046001600160501b03166123d0565b600454600160a01b90046001600160501b03165b6001600160501b0316613847565b613473565b8160c0018181516123f4919061495a565b6001600160701b031690525083156124485760065461242a906123de9083908690600160a01b90046001600160501b031661386b565b8160200181815161243b919061495a565b6001600160701b03169052505b6124528185613424565b949350505050565b6000828152600160208190526040909120015461247681612111565b6124808383613886565b505050565b6001600160a01b03811633146124f55760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610936565b6109498282613930565b60004282602001516001600160501b03161115612549574282602001516001600160501b031661252f919061498d565b825161254491906001600160501b03166149a0565b61068d565b600092915050565b6000806000806000612561612128565b600061256d600461396b565b9050600061257b600561396b565b90506000612589600661396b565b9050600081612598848661497a565b6125a2919061497a565b90506125ac6139e6565b6006546005546004546001600160501b03928316926125ce92811691166149e1565b6125d891906149e1565b6001600160501b03169990985092965090945092509050565b60005460ff16156121265760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610936565b6007546000906126549085908590859063ffffffff161515613afc565b90508060600151600014612675578051606082015161267591600591613ebc565b60808101511561269657612696600460000182602001518360800151613ebc565b60a0810151156126b7576126b7600460020182604001518360a00151613ebc565b50505050565b6126c5613f6b565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6127176125f1565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586126f23390565b6000612756611753565b61275f42613fb4565b6127699190614a01565b9050612775828261401b565b60405165ffffffffffff821681526001600160a01b038316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b60006127ca8261409a565b6127d342613fb4565b6127dd9190614a01565b90506127e9828261361a565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9101610cf8565b6000808061283e670de0b6b3a7640000856149cd565b90506000612854670de0b6b3a764000086614a20565b91959194509092505050565b6040516336b87bd760e11b81526001600160a01b0382811660048301526000917f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc590911690636d70f7ae90602401602060405180830381865afa1580156128cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ef9190614907565b8061068d5750604051637d3374e560e01b81526001600160a01b0383811660048301527f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc51690637d3374e590602401602060405180830381865afa15801561295b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068d9190614907565b600754600090600160201b900460ff16612a0f57600a54604051634c5b725f60e01b81526001600160a01b038581166004830152602482019290925290831690634c5b725f906044015b602060405180830381865afa1580156129e6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a0a9190614a34565b611307565b604051630706983f60e51b81526001600160a01b03848116600483015283169063e0d307e0906024015b602060405180830381865afa158015612a56573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113079190614a34565b612a8261451f565b6001600160a01b0384166000908152600c60209081526040808320815160e08101835281546001600160701b038082168352600160701b91829004811695830195909552600183015480861694830194909452830490931660608401528392916080830190600160e01b900460ff166002811115612b0257612b02614703565b6002811115612b1357612b13614703565b8152600291909101546001600160701b038082166020840152600160701b90910416604090910152905060008080612b496136da565b925092509250612b686123de858a8a612b625786613847565b85613847565b8460c001818151612b79919061495a565b6001600160701b03169052508615612bb757612b996123de858a8461386b565b84602001818151612baa919061495a565b6001600160701b03169052505b6000612c15856113bc6113028d8c612bef577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e612c6b565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5612c6b565b90508085600001818151612c29919061495a565b6001600160701b031690525060c0850151600090612c4890839061493a565b600060c0880152959b6001600160701b039096169a509498505050505050505050565b600754600090600160201b900460ff16612cb957600a54604051634b669bff60e11b81526001600160a01b0385811660048301526024820192909252908316906396cd37fe906044016129c9565b604051631624e46160e21b81526001600160a01b038481166004830152831690635893918490602401612a39565b600081600003612cf957506000919050565b600754600160201b900460ff16612d195750670de0b6b3a7640000919050565b7f000000000000000000000000000000000000000000000000000000000076a7006000819003612d535750670de0b6b3a764000092915050565b611307612d69612d63854261498d565b836133a3565b670de0b6b3a76400006140d9565b600080612d85600b54612828565b90925090506001836002811115612d9e57612d9e614703565b03612ea357428203612daf57505050565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152612e2390612e1e90612e187f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e613070565b856140ef565b6133b8565b600580546001600160501b0392909216600160a01b02600160a01b600160f01b0319909216919091179055612e584282613823565b600554604051600160a01b9091046001600160501b031681527f1236d5f7290187e9ac80880b3e28a193a9f38bb8bdc8a8a197744d80c50ca3269060200160405180910390a1505050565b6002836002811115612eb757612eb7614703565b0361248057428103612ec857505050565b6000612ef37f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5613070565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150612f3d90612e1e9083856140ef565b60048054600160a01b600160f01b031916600160a01b6001600160501b03938416810291909117909155604080516060810182526006548085168252600160501b8104851660208301529290920490921691810191909152612fa490612e1e9083856140ef565b600680546001600160501b0392909216600160a01b02600160a01b600160f01b0319909216919091179055612fd98342613823565b600454600654604080516001600160501b03600160a01b94859004811682529390920490921660208201527f9eecdebdae54d18e0c665dae22811251367c65647ec09aa5e33f001a5c147d22910160405180910390a150505050565b60008260a001516130666123de848660a001518760c00151613057919061495a565b6001600160701b031690614197565b611307919061493a565b600754600090600160201b900460ff1661308d57612544826141ac565b816001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130cb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068d9190614a34565b60008060008460000361310a57506000915081905080613133565b6000808086156131285787925061312183886133a3565b915061312b565b50865b919450925090505b9250925092565b600080613145611bd1565b9150915061315a8165ffffffffffff16151590565b801561316d57504265ffffffffffff8216105b6131ca5760405162461bcd60e51b815260206004820152602860248201527f416363657373436f6e74726f6c3a207472616e736665722064656c6179206e6f6044820152671d081c185cdcd95960c21b6064820152608401610936565b6131e660006131e16003546001600160a01b031690565b613930565b6131f1600083613886565b5050600280546001600160d01b0319169055565b6001600160a01b0383166000908152600c602090815260409182902084518154928601516001600160701b039182166001600160e01b031994851617600160701b91831682021783559386015160018301805460608901519284169516949094179116909302929092178082556080850151859392909160ff60e01b1916600160e01b83600281111561329a5761329a614703565b021790555060a08201516002909101805460c0909301516001600160701b03908116600160701b026001600160e01b03199094169216919091179190911790556040516001600160a01b038416907fa6b1f21803a1471cf6981bffc4c07ff6610f2b88efc44f62b96dca65a63d3d279061331990841515815260200190565b60405180910390a2826001600160a01b03167f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a83600001518460200151856040015186606001518760a001516040516108c19594939291906001600160701b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b600061130783670de0b6b3a7640000846141f7565b60006001600160501b038211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201526530206269747360d01b6064820152608401610936565b5090565b8015613456576004546001600160501b03600160a01b9182900481166040850152600654919091041660608301525050565b6005546001600160501b03600160a01b9091041660408301525050565b60006001600160701b038211156134205760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663132206269747360c81b6064820152608401610936565b600082815260016020819052604090912001546134f881612111565b6124808383613930565b61212660008061401b565b8060000361351a57505050565b61353161352782846149cd565b612e1e904261497a565b83546001600160501b0391909116600160501b02600160501b600160a01b0319909116179092555050565b600063ffffffff8211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610936565b6135cb8282611220565b610949576135d881614215565b6135e3836020614227565b6040516020016135f4929190614a4d565b60408051601f198184030181529082905262461bcd60e51b8252610936916004016145aa565b600354600160d01b900465ffffffffffff16801561369d574265ffffffffffff8216101561367357600354600280546001600160d01b0316600160a01b90920465ffffffffffff16600160d01b0291909117905561369d565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec590600090a15b50600380546001600160a01b0316600160a01b65ffffffffffff948516026001600160d01b031617600160d01b9290931691909102919091179055565b6000806000806137097f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e613070565b905060006137367f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5613070565b9050600080613746600b54612828565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152919350915061378e9085846140ef565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b90910416918101919091526137d19085846140ef565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b90910416918101919091526138149086856140ef565b96509650965050505050909192565b8061383683670de0b6b3a76400006149a0565b613840919061497a565b600b555050565b6000806138628486604001516001600160701b0316856143c3565b95945050505050565b6000806138628486606001516001600160701b0316856143c3565b8161392657600061389f6003546001600160a01b031690565b6001600160a01b03161461390a5760405162461bcd60e51b815260206004820152602c60248201527f416363657373436f6e74726f6c3a2064656661756c742061646d696e20616c7260448201526b1958591e4819dc985b9d195960a21b6064820152608401610936565b600380546001600160a01b0319166001600160a01b0383161790555b61094982826143d9565b8115801561394b57506003546001600160a01b038281169116145b1561396157600380546001600160a01b03191690555b6109498282614444565b6040805160608101825282546001600160501b038082168352600160501b820481166020840152600160a01b909104169181019190915260009081906139b0906124ff565b90506139bb426133b8565b83546001600160501b0391909116600160501b02600160501b600160a01b0319909116179092555090565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a689190614a34565b6008600001819055507f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613acf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af39190614a34565b60095543600a55565b613b356040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60007f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316866001600160a01b031603613b77576000613bf9565b7f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b0316630fbc8f5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bf99190614a34565b905060007f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b0316876001600160a01b031603613c3d576000613cbf565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316630fbc8f5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cbf9190614a34565b90506000613ccd828461497a565b90506000806000807f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b03168c6001600160a01b031603613d19578a9350899150613db9565b7f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b03168c6001600160a01b031603613d5c575089915088613db9565b613d678b87876144ab565b613d728a87876144ea565b84613d7d878d6149a0565b613d8791906149cd565b935084613d94878c6149a0565b613d9e91906149cd565b9150613daa848c61498d565b9250613db6828b61498d565b90505b6000808a8015613dc857508815155b15613e855760075461271090613de49063ffffffff16876149a0565b613dee91906149cd565b9150600085118015613dfe575081155b15613e1c57604051633853986560e01b815260040160405180910390fd5b613e26828661498d565b60075490955061271090613e409063ffffffff16856149a0565b613e4a91906149cd565b9050600083118015613e5a575080155b15613e785760405163041c928b60e11b815260040160405180910390fd5b613e82818461498d565b92505b6040805160c0810182529586526020860196909652948401526060830152608082015260a081019190915298975050505050505050565b6040805160608101825284546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090613eff906124ff565b905081613f0c828561497a565b1015613f2a5760405162da056d60e81b815260040160405180910390fd5b613f3e84613f38838661497a565b8461350d565b613f47826133b8565b845469ffffffffffffffffffff19166001600160501b039190911617909355505050565b60005460ff166121265760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610936565b600065ffffffffffff8211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203460448201526538206269747360d01b6064820152608401610936565b6000614025611bd1565b6002805465ffffffffffff8616600160a01b026001600160d01b03199091166001600160a01b03881617179055915061406790508165ffffffffffff16151590565b15612480576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a960510990600090a1505050565b6000806140a5611753565b90508065ffffffffffff168365ffffffffffff16116140c857612a0a8382614ac2565b61130765ffffffffffff8416620697805b60008183106140e85781611307565b5090919050565b60008260000361410d575060408301516001600160501b0316611307565b600061412685602001516001600160501b0316426140d9565b905082811161414457505060408301516001600160501b0316611307565b6000614150848361498d565b90506141758587600001516001600160501b03168361416f91906149a0565b906133a3565b86604001516001600160501b031661418d919061497a565b9695505050505050565b60006113078383670de0b6b3a76400006141f7565b60007f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316826001600160a01b0316146141ef5760095461068d565b505060085490565b600082600019048411830215820261420e57600080fd5b5091020490565b606061068d6001600160a01b03831660145b606060006142368360026149a0565b61424190600261497a565b67ffffffffffffffff811115614259576142596148db565b6040519080825280601f01601f191660200182016040528015614283576020820181803683370190505b509050600360fc1b8160008151811061429e5761429e6148f1565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106142cd576142cd6148f1565b60200101906001600160f81b031916908160001a90535060006142f18460026149a0565b6142fc90600161497a565b90505b6001811115614374576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614330576143306148f1565b1a60f81b828281518110614346576143466148f1565b60200101906001600160f81b031916908160001a90535060049490941c9361436d81614ae1565b90506142ff565b5083156113075760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610936565b60006124526143d2848461498d565b8590614197565b6143e38282611220565b6109495760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b61444e8282611220565b156109495760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b82158015906144cc575081158015906144cc5750806144ca83856149a0565b105b1561248057604051633853986560e01b815260040160405180910390fd5b81158015906145015750806144ff83856149a0565b105b156124805760405163041c928b60e11b815260040160405180910390fd5b6040805160e08101825260008082526020820181905291810182905260608101829052906080820190815260006020820181905260409091015290565b60006020828403121561456e57600080fd5b81356001600160e01b03198116811461130757600080fd5b60005b838110156145a1578181015183820152602001614589565b50506000910152565b60208152600082518060208401526145c9816040850160208701614586565b601f01601f19169190910160400192915050565b80356001600160a01b03811681146145f457600080fd5b919050565b6000806040838503121561460c57600080fd5b614615836145dd565b946020939093013593505050565b60006020828403121561463557600080fd5b5035919050565b6000806040838503121561464f57600080fd5b8235915061465f602084016145dd565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156146a95783516001600160a01b031683529284019291840191600101614684565b50909695505050505050565b6000806000606084860312156146ca57600080fd5b6146d3846145dd565b95602085013595506040909401359392505050565b6000602082840312156146fa57600080fd5b611307826145dd565b634e487b7160e01b600052602160045260246000fd5b6001600160701b038082511683528060208301511660208401528060408301511660408401528060608301511660608401525060808101516003811061476f57634e487b7160e01b600052602160045260246000fd5b8060808401525060a081015161479060a08401826001600160701b03169052565b5060c081015161248060c08401826001600160701b03169052565b60e0810161068d8284614719565b6000602082840312156147cb57600080fd5b813565ffffffffffff8116811461130757600080fd5b61010081016147f08285614719565b8260e08301529392505050565b80151581146106a657600080fd5b600080600080600060a0868803121561482357600080fd5b61482c866145dd565b94506020860135935060408601359250606086013591506080860135614851816147fd565b809150509295509295909350565b815180516001600160501b039081168352602080830151821681850152604092830151821683850152808501518051831660608601528082015183166080860152830151821660a0850152828501518051831660c086015290810151821660e085015291820151166101008301526101208201905b5092915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561491957600080fd5b8151611307816147fd565b634e487b7160e01b600052601160045260246000fd5b6001600160701b038281168282160390808211156148d4576148d4614924565b6001600160701b038181168382160190808211156148d4576148d4614924565b8082018082111561068d5761068d614924565b8181038181111561068d5761068d614924565b808202811582820484141761068d5761068d614924565b634e487b7160e01b600052601260045260246000fd5b6000826149dc576149dc6149b7565b500490565b6001600160501b038181168382160190808211156148d4576148d4614924565b65ffffffffffff8181168382160190808211156148d4576148d4614924565b600082614a2f57614a2f6149b7565b500690565b600060208284031215614a4657600080fd5b5051919050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351614a85816017850160208801614586565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351614ab6816028840160208801614586565b01602801949350505050565b65ffffffffffff8281168282160390808211156148d4576148d4614924565b600081614af057614af0614924565b50600019019056fea2646970667358221220759dc3b88a3e08ba309d211f431f2485ef8307884fb25ec991b4b67724d1cfe864736f6c63430008130033

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

000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc50000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000000000

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

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca
Arg [1] : 000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e
Arg [2] : 000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000190
Arg [4] : 000000000000000000000000000000000000000000000000000000000076a700
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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