More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x05a94ad4fc1dc16b5f9b8a2d74cf4cbe9cbcf8a606e72a85368b4d40469d5f96 | Migrate | (pending) | 8 mins ago | IN | 0 ETH | (Pending) | |||
Migrate | 21840337 | 2 days ago | IN | 0 ETH | 0.00037187 | ||||
Migrate | 21823589 | 4 days ago | IN | 0 ETH | 0.00024056 | ||||
Migrate | 21822399 | 4 days ago | IN | 0 ETH | 0.00029829 | ||||
Migrate | 21811698 | 6 days ago | IN | 0 ETH | 0.00022853 | ||||
Migrate | 21809544 | 6 days ago | IN | 0 ETH | 0.0001647 | ||||
Migrate | 21786090 | 9 days ago | IN | 0 ETH | 0.00021111 | ||||
Migrate | 21719547 | 19 days ago | IN | 0 ETH | 0.00047692 | ||||
Migrate | 21713316 | 20 days ago | IN | 0 ETH | 0.00068216 | ||||
Migrate | 21672488 | 25 days ago | IN | 0 ETH | 0.00121554 | ||||
Migrate | 21669895 | 26 days ago | IN | 0 ETH | 0.00185092 | ||||
Migrate | 21666758 | 26 days ago | IN | 0 ETH | 0.00502206 | ||||
Migrate | 21661335 | 27 days ago | IN | 0 ETH | 0.01231647 | ||||
Migrate | 21641455 | 30 days ago | IN | 0 ETH | 0.00057539 | ||||
Migrate | 21639054 | 30 days ago | IN | 0 ETH | 0.0015067 | ||||
Migrate | 21568361 | 40 days ago | IN | 0 ETH | 0.00182152 | ||||
Migrate | 21568208 | 40 days ago | IN | 0 ETH | 0.00258184 | ||||
Migrate | 21560980 | 41 days ago | IN | 0 ETH | 0.00091589 | ||||
Migrate | 21523060 | 46 days ago | IN | 0 ETH | 0.00176909 | ||||
Migrate | 21511395 | 48 days ago | IN | 0 ETH | 0.00044408 | ||||
Migrate | 21495517 | 50 days ago | IN | 0 ETH | 0.00097297 | ||||
Migrate | 21489907 | 51 days ago | IN | 0 ETH | 0.00056443 | ||||
Migrate | 21469507 | 54 days ago | IN | 0 ETH | 0.00086123 | ||||
Migrate | 21450958 | 56 days ago | IN | 0 ETH | 0.00113797 | ||||
Migrate | 21441800 | 58 days ago | IN | 0 ETH | 0.00141617 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
18671931 | 445 days ago | 0.00707205 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Staking
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 75000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import {LinkTokenInterface} from '@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol'; import {TypeAndVersionInterface} from '@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol'; import {AggregatorV3Interface} from '@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol'; import {ConfirmedOwner} from '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol'; import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol'; import {MerkleProof} from '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol'; import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol'; import {Math} from '@openzeppelin/contracts/utils/math/Math.sol'; import {IStaking} from './interfaces/IStaking.sol'; import {IStakingOwner} from './interfaces/IStakingOwner.sol'; import {IMerkleAccessController} from './interfaces/IMerkleAccessController.sol'; import {IAlertsController} from './interfaces/IAlertsController.sol'; import {IMigratable} from './interfaces/IMigratable.sol'; import {StakingPoolLib} from './StakingPoolLib.sol'; import {RewardLib, SafeCast} from './RewardLib.sol'; contract Staking is IStaking, IStakingOwner, IMigratable, IMerkleAccessController, IAlertsController, ConfirmedOwner, TypeAndVersionInterface, Pausable { using StakingPoolLib for StakingPoolLib.Pool; using RewardLib for RewardLib.Reward; using SafeCast for uint256; /// @notice This struct defines the params required by the Staking contract's /// constructor. struct PoolConstructorParams { /// @notice The LINK Token LinkTokenInterface LINKAddress; /// @notice The feed being monitored when raising alerts AggregatorV3Interface monitoredFeed; /// @notice The initial maximum total stake amount across all stakers uint256 initialMaxPoolSize; /// @notice The initial maximum stake amount for a single community staker uint256 initialMaxCommunityStakeAmount; /// @notice The initial maximum stake amount for a single node operator uint256 initialMaxOperatorStakeAmount; /// @notice The minimum stake amount that a community staker can stake uint256 minCommunityStakeAmount; /// @notice The minimum stake amount that an operator can stake uint256 minOperatorStakeAmount; /// @notice The number of seconds until the feed is considered stale /// and the priority period begins. uint256 priorityPeriodThreshold; /// @notice The number of seconds until the priority period ends /// and the regular period begins. uint256 regularPeriodThreshold; /// @notice The amount of LINK to reward an operator who /// raises an alert in the priority period. uint256 maxAlertingRewardAmount; /// @notice The minimum number of node operators required to initialize the /// staking pool. uint256 minInitialOperatorCount; /// @notice The minimum reward duration after pool config updates and pool /// reward extensions uint256 minRewardDuration; /// @notice The duration of earned rewards to slash when an alert is raised uint256 slashableDuration; /// @notice Used to calculate delegated stake amount /// = amount / delegation rate denominator = 100% / 100 = 1% uint256 delegationRateDenominator; } /// @notice The amount to divide an alerter's stake amount when /// calculating their reward for raising an alert. uint256 private constant ALERTING_REWARD_STAKED_AMOUNT_DENOMINATOR = 5; LinkTokenInterface private immutable i_LINK; StakingPoolLib.Pool private s_pool; RewardLib.Reward private s_reward; /// @notice The ETH USD feed that alerters can raise alerts for. AggregatorV3Interface private immutable i_monitoredFeed; /// @notice The proposed address stakers will migrate funds to address private s_proposedMigrationTarget; /// @notice The timestamp of when the migration target was proposed at uint256 private s_proposedMigrationTargetAt; /// @notice The address stakers can migrate their funds to address private s_migrationTarget; /// @notice The round ID of the last feed round an alert was raised uint256 private s_lastAlertedRoundId; /// @notice The merkle root of the merkle tree generated from the list /// of staker addresses with early acccess. bytes32 private s_merkleRoot; /// @notice The number of seconds until the feed is considered stale /// and the priority period begins. uint256 private immutable i_priorityPeriodThreshold; /// @notice The number of seconds until the priority period ends /// and the regular period begins. uint256 private immutable i_regularPeriodThreshold; /// @notice The amount of LINK to reward an operator who /// raises an alert in the priority period. uint256 private immutable i_maxAlertingRewardAmount; /// @notice The minimum stake amount that a node operator can stake uint256 private immutable i_minOperatorStakeAmount; /// @notice The minimum stake amount that a community staker can stake uint256 private immutable i_minCommunityStakeAmount; /// @notice The minimum number of node operators required to initialize the /// staking pool. uint256 private immutable i_minInitialOperatorCount; /// @notice The minimum reward duration after pool config updates and pool /// reward extensions uint256 private immutable i_minRewardDuration; /// @notice The duration of earned rewards to slash when an alert is raised uint256 private immutable i_slashableDuration; /// @notice Used to calculate delegated stake amount /// = amount / delegation rate denominator = 100% / 100 = 1% uint256 private immutable i_delegationRateDenominator; constructor(PoolConstructorParams memory params) ConfirmedOwner(msg.sender) { if (address(params.LINKAddress) == address(0)) revert InvalidZeroAddress(); if (address(params.monitoredFeed) == address(0)) revert InvalidZeroAddress(); if (params.delegationRateDenominator == 0) revert InvalidDelegationRate(); if (RewardLib.REWARD_PRECISION % params.delegationRateDenominator > 0) revert InvalidDelegationRate(); if (params.regularPeriodThreshold <= params.priorityPeriodThreshold) revert InvalidRegularPeriodThreshold(); if (params.minOperatorStakeAmount == 0) revert InvalidMinOperatorStakeAmount(); if (params.minOperatorStakeAmount > params.initialMaxOperatorStakeAmount) revert InvalidMinOperatorStakeAmount(); if (params.minCommunityStakeAmount > params.initialMaxCommunityStakeAmount) revert InvalidMinCommunityStakeAmount(); if (params.maxAlertingRewardAmount > params.initialMaxOperatorStakeAmount) revert InvalidMaxAlertingRewardAmount(); s_pool._setConfig( params.initialMaxPoolSize, params.initialMaxCommunityStakeAmount, params.initialMaxOperatorStakeAmount ); i_LINK = params.LINKAddress; i_monitoredFeed = params.monitoredFeed; i_priorityPeriodThreshold = params.priorityPeriodThreshold; i_regularPeriodThreshold = params.regularPeriodThreshold; i_maxAlertingRewardAmount = params.maxAlertingRewardAmount; i_minOperatorStakeAmount = params.minOperatorStakeAmount; i_minCommunityStakeAmount = params.minCommunityStakeAmount; i_minInitialOperatorCount = params.minInitialOperatorCount; i_minRewardDuration = params.minRewardDuration; i_slashableDuration = params.slashableDuration; i_delegationRateDenominator = params.delegationRateDenominator; } // ======================= // TypeAndVersionInterface // ======================= /// @inheritdoc TypeAndVersionInterface function typeAndVersion() external pure override returns (string memory) { return 'Staking 0.1.0'; } // ================= // IMerkleAccessController // ================= /// @inheritdoc IMerkleAccessController function hasAccess(address staker, bytes32[] memory proof) external view override returns (bool) { if (s_merkleRoot == bytes32(0)) return true; return MerkleProof.verify(proof, s_merkleRoot, keccak256(abi.encode(staker))); } /// @inheritdoc IMerkleAccessController function setMerkleRoot(bytes32 newMerkleRoot) external override onlyOwner { s_merkleRoot = newMerkleRoot; emit MerkleRootChanged(newMerkleRoot); } /// @inheritdoc IMerkleAccessController function getMerkleRoot() external view override returns (bytes32) { return s_merkleRoot; } // ============= // IStakingOwner // ============= /// @inheritdoc IStakingOwner function setPoolConfig( uint256 maxPoolSize, uint256 maxCommunityStakeAmount, uint256 maxOperatorStakeAmount ) external override(IStakingOwner) onlyOwner whenActive { s_pool._setConfig( maxPoolSize, maxCommunityStakeAmount, maxOperatorStakeAmount ); s_reward._updateDuration( maxPoolSize, s_pool._getTotalStakedAmount(), uint256(s_reward.base.rate), i_minRewardDuration, getAvailableReward(), getTotalDelegatedAmount() ); } /// @inheritdoc IStakingOwner function setFeedOperators(address[] calldata operators) external override(IStakingOwner) onlyOwner { s_pool._setFeedOperators(operators); } /// @inheritdoc IStakingOwner function start(uint256 amount, uint256 initialRewardRate) external override(IStakingOwner) onlyOwner { if (s_merkleRoot == bytes32(0)) revert MerkleRootNotSet(); s_pool._open(i_minInitialOperatorCount); // We need to transfer LINK balance before we initialize the reward to // calculate the new reward expiry timestamp. i_LINK.transferFrom(msg.sender, address(this), amount); s_reward._initialize( uint256(s_pool.limits.maxPoolSize), initialRewardRate, i_minRewardDuration, getAvailableReward() ); } /// @inheritdoc IStakingOwner function conclude() external override(IStakingOwner) onlyOwner whenActive { s_reward._release( s_pool._getTotalStakedAmount(), getTotalDelegatedAmount() ); s_pool._close(); } /// @inheritdoc IStakingOwner function addReward(uint256 amount) external override(IStakingOwner) onlyOwner whenActive { // We need to transfer LINK balance before we recalculate the reward expiry // timestamp so the new amount is accounted for. i_LINK.transferFrom(msg.sender, address(this), amount); s_reward._updateDuration( uint256(s_pool.limits.maxPoolSize), s_pool._getTotalStakedAmount(), uint256(s_reward.base.rate), i_minRewardDuration, getAvailableReward(), getTotalDelegatedAmount() ); emit RewardLib.RewardAdded(amount); } /// @inheritdoc IStakingOwner function withdrawUnusedReward() external override(IStakingOwner) onlyOwner whenInactive { uint256 unusedRewards = getAvailableReward() - uint256(s_reward.reserved.base) - uint256(s_reward.reserved.delegated); emit RewardLib.RewardWithdrawn(unusedRewards); // msg.sender is the owner address as only the owner can call this function i_LINK.transfer(msg.sender, unusedRewards); } /// @dev Required conditions for adding operators: /// - Operators can only be added to the pool if they have no prior stake. /// - Operators can only be readded to the pool if they have no removed /// stake. /// - Operators cannot be added to the pool after staking ends (either through /// conclusion or through reward expiry). /// @inheritdoc IStakingOwner function addOperators(address[] calldata operators) external override(IStakingOwner) onlyOwner { // If reward was initialized (meaning the pool was active) but the pool is // no longer active we want to prevent adding new operators. if (s_reward.startTimestamp > 0 && !isActive()) revert StakingPoolLib.InvalidPoolStatus(false, true); s_pool._addOperators(operators); } /// @inheritdoc IStakingOwner function removeOperators(address[] calldata operators) external override(IStakingOwner) onlyOwner whenActive { // Accumulate delegation rewards before removing operators as this affects // rewards that are distributed to remaining operators. s_reward._accumulateDelegationRewards(getTotalDelegatedAmount()); for (uint256 i; i < operators.length; i++) { address operator = operators[i]; StakingPoolLib.Staker memory staker = s_pool.stakers[operator]; if (!staker.isOperator) revert StakingPoolLib.OperatorDoesNotExist(operator); // Operator must not be on the feed if (staker.isFeedOperator) revert StakingPoolLib.OperatorIsAssignedToFeed(operator); uint256 principal = staker.stakedAmount; // An operator with stake is a delegate if (principal > 0) { // The operator's rewards are forfeited when they are removed // Unreserve operator's earned base reward s_reward.reserved.base -= getBaseReward(operator)._toUint96(); // Unreserve operator's future base reward s_reward.reserved.base -= s_reward ._calculateReward(principal, s_reward._getRemainingDuration()) ._toUint96(); // Unreserve operator's earned delegation reward. We don't need to // unreserve future delegation rewards because they will be split by // other operators. s_reward.reserved.delegated -= getDelegationReward(operator) ._toUint96(); s_reward.delegated.delegatesCount -= 1; delete s_pool.stakers[operator].stakedAmount; uint96 castPrincipal = principal._toUint96(); s_pool.state.totalOperatorStakedAmount -= castPrincipal; // Only the operator's principal is withdrawable after they are removed s_pool.stakers[operator].removedStakeAmount = castPrincipal; s_pool.totalOperatorRemovedAmount += castPrincipal; // We need to reset operator's missed base rewards in case they decide // to stake as a community staker using the same address. It's fine to // not reset missed delegated rewards, because a removed operator // cannot be re-added as operator again. delete s_reward.missed[operator].base; } s_pool.stakers[operator].isOperator = false; emit StakingPoolLib.OperatorRemoved(operator, principal); } s_pool.state.operatorsCount -= operators.length._toUint8(); } /// @inheritdoc IStakingOwner function changeRewardRate(uint256 newRate) external override onlyOwner whenActive { if (newRate == 0) revert(); uint256 totalDelegatedAmount = getTotalDelegatedAmount(); s_reward._accumulateDelegationRewards(totalDelegatedAmount); s_reward._accumulateBaseRewards(); s_reward._updateDuration( uint256(s_pool.limits.maxPoolSize), s_pool._getTotalStakedAmount(), newRate, i_minRewardDuration, getAvailableReward(), totalDelegatedAmount ); emit RewardLib.RewardRateChanged(newRate); } /// @inheritdoc IStakingOwner function emergencyPause() external override(IStakingOwner) onlyOwner { _pause(); } /// @inheritdoc IStakingOwner function emergencyUnpause() external override(IStakingOwner) onlyOwner { _unpause(); } /// @inheritdoc IStakingOwner function getFeedOperators() external view override(IStakingOwner) returns (address[] memory) { return s_pool.feedOperators; } // =========== // IMigratable // =========== /// @inheritdoc IMigratable function getMigrationTarget() external view override(IMigratable) returns (address) { return s_migrationTarget; } /// @inheritdoc IMigratable function proposeMigrationTarget(address migrationTarget) external override(IMigratable) onlyOwner { if ( migrationTarget.code.length == 0 || migrationTarget == address(this) || s_proposedMigrationTarget == migrationTarget || s_migrationTarget == migrationTarget || !IERC165(migrationTarget).supportsInterface(this.onTokenTransfer.selector) ) revert InvalidMigrationTarget(); s_migrationTarget = address(0); s_proposedMigrationTarget = migrationTarget; s_proposedMigrationTargetAt = block.timestamp; emit MigrationTargetProposed(migrationTarget); } /// @inheritdoc IMigratable function acceptMigrationTarget() external override(IMigratable) onlyOwner { if (s_proposedMigrationTarget == address(0)) revert InvalidMigrationTarget(); if (block.timestamp < (uint256(s_proposedMigrationTargetAt) + 7 days)) revert AccessForbidden(); s_migrationTarget = s_proposedMigrationTarget; s_proposedMigrationTarget = address(0); emit MigrationTargetAccepted(s_migrationTarget); } /// @inheritdoc IMigratable function migrate(bytes calldata data) external override(IMigratable) whenInactive { if (s_migrationTarget == address(0)) revert InvalidMigrationTarget(); (uint256 amount, uint256 baseReward, uint256 delegationReward) = _exit( msg.sender ); emit Migrated(msg.sender, amount, baseReward, delegationReward, data); i_LINK.transferAndCall( s_migrationTarget, uint256(amount + baseReward + delegationReward), abi.encode(msg.sender, data) ); } // ================= // IAlertsController // ================= /// @inheritdoc IAlertsController function raiseAlert() external override(IAlertsController) whenActive { uint256 stakedAmount = getStake(msg.sender); if (stakedAmount == 0) revert AccessForbidden(); (uint256 roundId, , , uint256 lastFeedUpdatedAt, ) = i_monitoredFeed .latestRoundData(); if (roundId == s_lastAlertedRoundId) revert AlertAlreadyExists(roundId); if (block.timestamp < lastFeedUpdatedAt + i_priorityPeriodThreshold) revert AlertInvalid(); bool isInPriorityPeriod = block.timestamp < lastFeedUpdatedAt + i_regularPeriodThreshold; if (isInPriorityPeriod && !s_pool._isOperator(msg.sender)) revert AlertInvalid(); s_lastAlertedRoundId = roundId; // There is a risk that this might get us below the total amount of // reserved if the reward amount slashed is greater than LINK // balance in the pool. This is an extreme edge case that will only occur /// if an alert is raised many times such that it completely depletes the // available rewards in the pool. As this is an unlikely scenario, the // contract avoids adding an extra check to minimize gas costs. // There is a similar edge case when the total slashed amount is less than // the alerting reward. This can happen because slashed amounts are capped to // earned rewards so far. The result is a net outflow of rewards from the // staking pool up to the max alerting reward amount in the worst case. // This is acceptable and in practice has little to no impact to staking. uint256 rewardAmount = _calculateAlertingRewardAmount( stakedAmount, isInPriorityPeriod ); emit AlertRaised(msg.sender, roundId, rewardAmount); // We need to transfer the rewards out before recalculating the new reward // expiry timestamp i_LINK.transfer(msg.sender, rewardAmount); s_reward._slashOnFeedOperators( i_minOperatorStakeAmount, i_slashableDuration, s_pool.feedOperators, s_pool.stakers, getTotalDelegatedAmount() ); s_reward._updateDuration( uint256(s_pool.limits.maxPoolSize), s_pool._getTotalStakedAmount(), uint256(s_reward.base.rate), 0, getAvailableReward(), getTotalDelegatedAmount() ); } /// @inheritdoc IAlertsController function canAlert(address alerter) external view override(IAlertsController) returns (bool) { if (getStake(alerter) == 0) return false; if (!isActive()) return false; (uint256 roundId, , , uint256 updatedAt, ) = i_monitoredFeed .latestRoundData(); if (roundId == s_lastAlertedRoundId) return false; // nobody can (feed is not stale) if (block.timestamp < updatedAt + i_priorityPeriodThreshold) return false; // all stakers can (regular alerters) if (block.timestamp >= updatedAt + i_regularPeriodThreshold) return true; return s_pool._isOperator(alerter); // only operators can (priority alerters) } // ======== // IStaking // ======== /// @inheritdoc IStaking function unstake() external override(IStaking) whenInactive { (uint256 amount, uint256 baseReward, uint256 delegationReward) = _exit( msg.sender ); emit Unstaked(msg.sender, amount, baseReward, delegationReward); i_LINK.transfer(msg.sender, amount + baseReward + delegationReward); } /// @inheritdoc IStaking function withdrawRemovedStake() external override(IStaking) whenInactive { uint256 amount = s_pool.stakers[msg.sender].removedStakeAmount; if (amount == 0) revert StakingPoolLib.StakeNotFound(msg.sender); s_pool.totalOperatorRemovedAmount -= amount; delete s_pool.stakers[msg.sender].removedStakeAmount; emit Unstaked(msg.sender, amount, 0, 0); i_LINK.transfer(msg.sender, amount); } /// @inheritdoc IStaking function getStake(address staker) public view override(IStaking) returns (uint256) { return s_pool.stakers[staker].stakedAmount; } /// @inheritdoc IStaking function isOperator(address staker) external view override(IStaking) returns (bool) { return s_pool._isOperator(staker); } /// @inheritdoc IStaking function isActive() public view override(IStaking) returns (bool) { return s_pool.state.isOpen && !s_reward._isDepleted(); } /// @inheritdoc IStaking function getMaxPoolSize() external view override(IStaking) returns (uint256) { return uint256(s_pool.limits.maxPoolSize); } /// @inheritdoc IStaking function getCommunityStakerLimits() external view override(IStaking) returns (uint256, uint256) { return ( i_minCommunityStakeAmount, uint256(s_pool.limits.maxCommunityStakeAmount) ); } /// @inheritdoc IStaking function getOperatorLimits() external view override(IStaking) returns (uint256, uint256) { return ( i_minOperatorStakeAmount, uint256(s_pool.limits.maxOperatorStakeAmount) ); } /// @inheritdoc IStaking function getRewardTimestamps() external view override(IStaking) returns (uint256, uint256) { return (uint256(s_reward.startTimestamp), uint256(s_reward.endTimestamp)); } /// @inheritdoc IStaking function getRewardRate() external view override(IStaking) returns (uint256) { return uint256(s_reward.base.rate); } /// @inheritdoc IStaking function getDelegationRateDenominator() external view override(IStaking) returns (uint256) { return i_delegationRateDenominator; } /// @inheritdoc IStaking function getAvailableReward() public view override(IStaking) returns (uint256) { return i_LINK.balanceOf(address(this)) - s_pool._getTotalStakedAmount() - s_pool.totalOperatorRemovedAmount; } /// @inheritdoc IStaking function getBaseReward(address staker) public view override(IStaking) returns (uint256) { uint256 stake = s_pool.stakers[staker].stakedAmount; if (stake == 0) return 0; if (s_pool._isOperator(staker)) { return s_reward._getOperatorEarnedBaseRewards(staker, stake); } return s_reward._calculateAccruedBaseRewards( RewardLib._getNonDelegatedAmount(stake, i_delegationRateDenominator) ) - uint256(s_reward.missed[staker].base); } /// @inheritdoc IStaking function getDelegationReward(address staker) public view override(IStaking) returns (uint256) { StakingPoolLib.Staker memory stakerAccount = s_pool.stakers[staker]; if (!stakerAccount.isOperator) return 0; if (stakerAccount.stakedAmount == 0) return 0; return s_reward._getOperatorEarnedDelegatedRewards( staker, getTotalDelegatedAmount() ); } /// @inheritdoc IStaking function getTotalDelegatedAmount() public view override(IStaking) returns (uint256) { return RewardLib._getDelegatedAmount( s_pool.state.totalCommunityStakedAmount, i_delegationRateDenominator ); } /// @inheritdoc IStaking function getDelegatesCount() external view override(IStaking) returns (uint256) { return uint256(s_reward.delegated.delegatesCount); } /// @inheritdoc IStaking function getTotalStakedAmount() external view override(IStaking) returns (uint256) { return s_pool._getTotalStakedAmount(); } /// @inheritdoc IStaking function getTotalCommunityStakedAmount() external view override(IStaking) returns (uint256) { return s_pool.state.totalCommunityStakedAmount; } /// @inheritdoc IStaking function getTotalRemovedAmount() external view override(IStaking) returns (uint256) { return s_pool.totalOperatorRemovedAmount; } /// @inheritdoc IStaking function getEarnedBaseRewards() external view override(IStaking) returns (uint256) { return s_reward._getEarnedBaseRewards( s_pool._getTotalStakedAmount(), getTotalDelegatedAmount() ); } /// @inheritdoc IStaking function getEarnedDelegationRewards() external view override(IStaking) returns (uint256) { return s_reward._getEarnedDelegationRewards(getTotalDelegatedAmount()); } /// @inheritdoc IStaking function isPaused() external view override(IStaking) returns (bool) { return paused(); } /// @inheritdoc IStaking function getChainlinkToken() public view override(IStaking) returns (address) { return address(i_LINK); } /// @inheritdoc IStaking function getMonitoredFeed() external view override returns (address) { return address(i_monitoredFeed); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @param sender Address of the sender * @param amount Amount of LINK sent (specified in wei) * @param data Optional payload containing a Staking Allowlist Merkle proof */ function onTokenTransfer( address sender, uint256 amount, bytes memory data ) external validateFromLINK whenNotPaused whenActive { if (amount < RewardLib.REWARD_PRECISION) revert StakingPoolLib.InsufficientStakeAmount(RewardLib.REWARD_PRECISION); // TL;DR: Reward calculation and delegation logic requires precise numbers // to avoid cumulative rounding errors. // Long explanation: // When users stake amounts that are rounded down to 0 after dividing // by the delegation rate denominator, not enough rewards are reserved for // the user. When the user then stakes enough times, small rounding errors // accumulate. This causes an integer underflow when unreserving rewards because // the total delegated amount returns a larger number than what individual // reserved amounts sum up to. uint256 remainder = amount % RewardLib.REWARD_PRECISION; if (remainder > 0) { amount -= remainder; i_LINK.transfer(sender, remainder); } if (s_pool._isOperator(sender)) { _stakeAsOperator(sender, amount); } else { // If a Merkle root is set, the sender should // prove that they are part of the merkle tree if (s_merkleRoot != bytes32(0)) { if (data.length == 0) revert AccessForbidden(); if ( !MerkleProof.verify( abi.decode(data, (bytes32[])), s_merkleRoot, keccak256(abi.encode(sender)) ) ) revert AccessForbidden(); } _stakeAsCommunityStaker(sender, amount); } } // ======= // Private // ======= /// @notice Helper function for when a community staker enters the pool /// @param staker The staker address /// @param amount The amount of principal staked /// @dev When an operator is removed they can stake as a community staker. /// We allow that because the alternative (checking for removed stake before /// staking) is going to unnecessarily increase gas costs in 99.99% of the /// cases. function _stakeAsCommunityStaker(address staker, uint256 amount) private { uint256 currentStakedAmount = s_pool.stakers[staker].stakedAmount; uint256 newStakedAmount = currentStakedAmount + amount; // Check that the amount is greater than or equal to the minimum required if (newStakedAmount < i_minCommunityStakeAmount) revert StakingPoolLib.InsufficientStakeAmount(i_minCommunityStakeAmount); // Check that the amount is less than or equal to the maximum allowed uint256 maxCommunityStakeAmount = uint256( s_pool.limits.maxCommunityStakeAmount ); if (newStakedAmount > maxCommunityStakeAmount) revert StakingPoolLib.ExcessiveStakeAmount( maxCommunityStakeAmount - currentStakedAmount ); // Check if the amount supplied increases the total staked amount above // the maximum pool size uint256 remainingPoolSpace = s_pool._getRemainingPoolSpace(); if (amount > remainingPoolSpace) revert StakingPoolLib.ExcessiveStakeAmount(remainingPoolSpace); s_reward._accumulateDelegationRewards(getTotalDelegatedAmount()); uint256 extraNonDelegatedAmount = RewardLib._getNonDelegatedAmount( amount, i_delegationRateDenominator ); s_reward.missed[staker].base += s_reward ._calculateAccruedBaseRewards(extraNonDelegatedAmount) ._toUint96(); s_reward._reserve( extraNonDelegatedAmount, RewardLib._getDelegatedAmount(amount, i_delegationRateDenominator) ); s_pool.state.totalCommunityStakedAmount += amount._toUint96(); s_pool.stakers[staker].stakedAmount = newStakedAmount._toUint96(); emit Staked(staker, amount, newStakedAmount); } /// @notice Helper function for when an operator enters the pool /// @dev Function skips validating whether or not the operator stake /// amount will cause the total stake amount to exceed the maximum pool size. /// This is because the pool already reserves a fixed amount of space /// for each operator meaning that an operator staking cannot cause the /// total stake amount to exceed the maximum pool size. Each operator /// receives a reserved stake amount equal to the maxOperatorStakeAmount. /// This is done by deducting operatorCount * maxOperatorStakeAmount from the /// remaining pool space available for staking. /// @param staker The staker address /// @param amount The amount of principal staked function _stakeAsOperator(address staker, uint256 amount) private { StakingPoolLib.Staker storage operator = s_pool.stakers[staker]; uint256 currentStakedAmount = operator.stakedAmount; uint256 newStakedAmount = currentStakedAmount + amount; // Check that the amount is greater than or equal to the minimum required if (newStakedAmount < i_minOperatorStakeAmount) revert StakingPoolLib.InsufficientStakeAmount(i_minOperatorStakeAmount); // Check that the amount is less than or equal to the maximum allowed uint256 maxOperatorStakeAmount = uint256( s_pool.limits.maxOperatorStakeAmount ); if (newStakedAmount > maxOperatorStakeAmount) revert StakingPoolLib.ExcessiveStakeAmount( maxOperatorStakeAmount - currentStakedAmount ); // On first stake if (currentStakedAmount == 0) { s_reward._accumulateDelegationRewards(getTotalDelegatedAmount()); uint8 delegatesCount = s_reward.delegated.delegatesCount; // We are doing this check to unreserve any unused delegated rewards // prior to the first operator staking. After the rewards are unreserved // we reset the accumulated value so it doesn't count towards missed // rewards. // There is a known edge-case where, if no operator stakes throughout the // duration of the pool, we wouldn't unreserve unused delegation rewards. // In practice this shouldn't happen and, if it does, there are // operational workarounds to unreserve those rewards. if (delegatesCount == 0) { s_reward.reserved.delegated -= s_reward.delegated.cumulativePerDelegate; delete s_reward.delegated.cumulativePerDelegate; } s_reward.delegated.delegatesCount = delegatesCount + 1; s_reward.missed[staker].delegated = s_reward .delegated .cumulativePerDelegate; } s_reward.missed[staker].base += s_reward ._calculateAccruedBaseRewards(amount) ._toUint96(); s_pool.state.totalOperatorStakedAmount += amount._toUint96(); s_reward._reserve(amount, 0); s_pool.stakers[staker].stakedAmount = newStakedAmount._toUint96(); emit Staked(staker, amount, newStakedAmount); } /// @notice Helper function when staker exits the pool /// @param staker The staker address function _exit(address staker) private returns ( uint256, uint256, uint256 ) { StakingPoolLib.Staker memory stakerAccount = s_pool.stakers[staker]; if (stakerAccount.stakedAmount == 0) revert StakingPoolLib.StakeNotFound(staker); // If the pool isOpen that means that we haven't concluded it and stakers // got here because the reward depleted. In that case, the first user to // unstake will accumulate delegation and base rewards to save on cost for // others. if (s_pool.state.isOpen) { // Accumulate base and delegation rewards before unreserving rewards to // save gas costs. We can use the accumulated reward per micro LINK and // accumulated delegation reward to simplify reward calculations. s_reward._accumulateDelegationRewards(getTotalDelegatedAmount()); s_reward._accumulateBaseRewards(); delete s_pool.state.isOpen; } if (stakerAccount.isOperator) { s_pool.state.totalOperatorStakedAmount -= stakerAccount.stakedAmount; uint256 baseReward = s_reward._calculateConcludedBaseRewards( stakerAccount.stakedAmount, staker ); uint256 delegationReward = uint256( s_reward.delegated.cumulativePerDelegate ) - uint256(s_reward.missed[staker].delegated); delete s_pool.stakers[staker].stakedAmount; s_reward.reserved.base -= baseReward._toUint96(); s_reward.reserved.delegated -= delegationReward._toUint96(); return (stakerAccount.stakedAmount, baseReward, delegationReward); } else { s_pool.state.totalCommunityStakedAmount -= stakerAccount.stakedAmount; uint256 baseReward = s_reward._calculateConcludedBaseRewards( RewardLib._getNonDelegatedAmount( stakerAccount.stakedAmount, i_delegationRateDenominator ), staker ); delete s_pool.stakers[staker].stakedAmount; s_reward.reserved.base -= baseReward._toUint96(); return (stakerAccount.stakedAmount, baseReward, 0); } } /// @notice Calculates the reward amount an alerter will receive for raising /// a successful alert in the current alerting period. /// @param stakedAmount Amount of LINK staked by the alerter /// @param isInPriorityPeriod True if it is currently in the priority period /// @return rewardAmount Amount of LINK rewards to be paid to the alerter function _calculateAlertingRewardAmount( uint256 stakedAmount, bool isInPriorityPeriod ) private view returns (uint256) { if (isInPriorityPeriod) return i_maxAlertingRewardAmount; return Math.min( stakedAmount / ALERTING_REWARD_STAKED_AMOUNT_DENOMINATOR, i_maxAlertingRewardAmount ); } // ========= // Modifiers // ========= /// @dev Having a private function for the modifer saves on the contract size function _isActive() private view { if (!isActive()) revert StakingPoolLib.InvalidPoolStatus(false, true); } /// @dev Reverts if the staking pool is inactive (not open for staking or /// expired) modifier whenActive() { _isActive(); _; } /// @dev Reverts if the staking pool is active (open for staking) modifier whenInactive() { if (isActive()) revert StakingPoolLib.InvalidPoolStatus(true, false); _; } /// @dev Reverts if not sent from the LINK token modifier validateFromLINK() { if (msg.sender != getChainlinkToken()) revert SenderNotLinkToken(); _; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import {SafeCast} from './SafeCast.sol'; import {StakingPoolLib} from './StakingPoolLib.sol'; import {Math} from '@openzeppelin/contracts/utils/math/Math.sol'; library RewardLib { using SafeCast for uint256; /// @notice emitted when the reward is initialized for the first time /// @param rate the reward rate /// @param available the amount of rewards available for distribution in the /// staking pool /// @param startTimestamp the start timestamp when rewards are started /// @param endTimestamp the timestamp when the reward will run out event RewardInitialized( uint256 rate, uint256 available, uint256 startTimestamp, uint256 endTimestamp ); /// @notice emitted when owner changes the reward rate /// @param rate the new reward rate event RewardRateChanged(uint256 rate); /// @notice emitted when owner adds more rewards to the pool /// @param amountAdded the amount of LINK rewards added to the pool event RewardAdded(uint256 amountAdded); /// @notice emitted when owner withdraws unreserved rewards /// @param amount the amount of rewards withdrawn event RewardWithdrawn(uint256 amount); /// @notice emitted when an on feed operator gets slashed. /// Node operators are not slashed more than the amount of rewards they /// have earned. This means that a node operator that has not /// accumulated at least two weeks of rewards will be slashed /// less than an operator that has accumulated at least /// two weeks of rewards. event RewardSlashed( address[] operator, uint256[] slashedBaseRewards, uint256[] slashedDelegatedRewards ); /// @notice This error is thrown when the updated reward duration is less than a month error RewardDurationTooShort(); /// @notice This is the reward calculation precision variable. LINK token has the /// 1e18 multiplier which means that rewards are floored after 6 decimals /// points. Micro LINK is the smallest unit that is eligible for rewards. uint256 internal constant REWARD_PRECISION = 1e12; struct DelegatedRewards { // Count of delegates who are eligible for a share of a reward // This is always going to be less or equal to operatorsCount uint8 delegatesCount; // Tracks base reward amounts that goes to an operator as delegation rewards. // Used to correctly account for any changes in operator count, delegated amount, or reward rate. // Formula: duration * rate * amount uint96 cumulativePerDelegate; // Timestamp of the last time accumulate was called // `startTimestamp` <= `delegated.lastAccumulateTimestamp` uint32 lastAccumulateTimestamp; } struct BaseRewards { // Reward rate expressed in juels per second per micro LINK uint80 rate; // The cumulative LINK accrued per stake from past reward rates // expressed in juels per micro LINK // Formula: sum of (previousRate * elapsedDurationSinceLastAccumulate) uint96 cumulativePerMicroLINK; // Timestamp of the last time the base reward rate was accumulated uint32 lastAccumulateTimestamp; } struct MissedRewards { // Tracks missed base rewards that are deducted from late stakers uint96 base; // Tracks missed delegation rewards that are deducted from late delegates uint96 delegated; } struct ReservedRewards { // Tracks base reward amount reserved for stakers. This can be used after // `endTimestamp` to calculate unused amount. // This amount accumulates as the reward is utilized. // Formula: duration * rate * amount uint96 base; // Tracks delegated reward amount reserved for node operators. This can // be used after `endTimestamp` to calculate unused amount. // This amount accumulates as the reward is utilized. // Formula: duration * rate * amount uint96 delegated; } struct Reward { mapping(address => MissedRewards) missed; DelegatedRewards delegated; BaseRewards base; ReservedRewards reserved; // Timestamp when the reward stops accumulating. Has to support a very long // duration for scenarios with low reward rate. // `endTimestamp` >= `startTimestamp` uint256 endTimestamp; // Timestamp when the reward comes into effect // `startTimestamp` <= `endTimestamp` uint32 startTimestamp; } /// @notice initializes the reward with the defined parameters /// @param maxPoolSize maximum pool size that the reward is initialized with /// @param rate reward rate /// @param minRewardDuration the minimum duration rewards need to last for /// @param availableReward available reward amount /// @dev can only be called once. Any future reward changes have to be done /// using specific functions. function _initialize( Reward storage reward, uint256 maxPoolSize, uint256 rate, uint256 minRewardDuration, uint256 availableReward ) internal { if (reward.startTimestamp != 0) revert(); reward.base.rate = rate._toUint80(); uint32 blockTimestamp = block.timestamp._toUint32(); reward.startTimestamp = blockTimestamp; reward.delegated.lastAccumulateTimestamp = blockTimestamp; reward.base.lastAccumulateTimestamp = blockTimestamp; _updateDuration( reward, maxPoolSize, 0, rate, minRewardDuration, availableReward, 0 ); emit RewardInitialized( rate, availableReward, reward.startTimestamp, reward.endTimestamp ); } /// @return bool true if the reward is expired (end <= now) function _isDepleted(Reward storage reward) internal view returns (bool) { return reward.endTimestamp <= block.timestamp; } /// @notice Helper function to accumulate base rewards /// Accumulate reward per micro LINK before changing reward rate. /// This keeps rewards prior to rate change unaffected. function _accumulateBaseRewards(Reward storage reward) internal { uint256 cappedTimestamp = _getCappedTimestamp(reward); reward.base.cumulativePerMicroLINK += (uint256(reward.base.rate) * (cappedTimestamp - uint256(reward.base.lastAccumulateTimestamp))) ._toUint96(); reward.base.lastAccumulateTimestamp = cappedTimestamp._toUint32(); } /// @notice Helper function to accumulate delegation rewards /// @dev This function is necessary to correctly account for any changes in /// eligible operators, delegated amount or reward rate. function _accumulateDelegationRewards( Reward storage reward, uint256 delegatedAmount ) internal { reward.delegated.cumulativePerDelegate = _calculateAccruedDelegatedRewards( reward, delegatedAmount )._toUint96(); reward.delegated.lastAccumulateTimestamp = _getCappedTimestamp(reward) ._toUint32(); } /// @notice Helper function to calculate rewards /// @param amount a staked amount to calculate rewards for /// @param duration a duration that the specified amount receives rewards for /// @return rewardsAmount function _calculateReward( Reward storage reward, uint256 amount, uint256 duration ) internal view returns (uint256) { return (amount * uint256(reward.base.rate) * duration) / REWARD_PRECISION; } /// @notice Calculates the amount of delegated rewards accumulated so far. /// @dev This function takes into account the amount of delegated /// rewards accumulated from previous delegate counts and amounts and /// the latest additional value. function _calculateAccruedDelegatedRewards( Reward storage reward, uint256 totalDelegatedAmount ) internal view returns (uint256) { uint256 elapsedDurationSinceLastAccumulate = _isDepleted(reward) ? uint256(reward.endTimestamp) - uint256(reward.delegated.lastAccumulateTimestamp) : block.timestamp - uint256(reward.delegated.lastAccumulateTimestamp); return uint256(reward.delegated.cumulativePerDelegate) + _calculateReward( reward, totalDelegatedAmount, elapsedDurationSinceLastAccumulate ) / // We are doing this to keep track of delegated rewards prior to the // first operator staking. Math.max(uint256(reward.delegated.delegatesCount), 1); } /// @notice Calculates the amount of rewards accrued so far. /// @dev This function takes into account the amount of /// rewards accumulated from previous rates in addition to /// the rewards that will be accumulated based off the current rate /// over a given duration. function _calculateAccruedBaseRewards(Reward storage reward, uint256 amount) internal view returns (uint256) { uint256 elapsedDurationSinceLastAccumulate = _isDepleted(reward) ? (uint256(reward.endTimestamp) - uint256(reward.base.lastAccumulateTimestamp)) : block.timestamp - uint256(reward.base.lastAccumulateTimestamp); return (amount * (uint256(reward.base.cumulativePerMicroLINK) + uint256(reward.base.rate) * elapsedDurationSinceLastAccumulate)) / REWARD_PRECISION; } /// @notice We use a simplified reward calculation formula because we know that /// the reward is expired. We accumulate reward per micro LINK /// before concluding the pool so we can avoid reading additional storage /// variables. function _calculateConcludedBaseRewards( Reward storage reward, uint256 amount, address staker ) internal view returns (uint256) { return (amount * uint256(reward.base.cumulativePerMicroLINK)) / REWARD_PRECISION - uint256(reward.missed[staker].base); } /// @notice Reserves staker rewards. This is necessary to make sure that /// there are always enough available LINK tokens for all stakers until the /// reward end timestamp. The amount is calculated for the remaining reward /// duration using the current reward rate. /// @param baseRewardAmount The amount of base rewards to reserve /// or unreserve for /// @param delegatedRewardAmount The amount of delegated rewards to reserve /// or unreserve for /// @param isReserving true if function should reserve more rewards. false will /// unreserve and deduct from the reserved total function _updateReservedRewards( Reward storage reward, uint256 baseRewardAmount, uint256 delegatedRewardAmount, bool isReserving ) private { uint256 duration = _getRemainingDuration(reward); uint96 deltaBaseReward = _calculateReward( reward, baseRewardAmount, duration )._toUint96(); uint96 deltaDelegatedReward = _calculateReward( reward, delegatedRewardAmount, duration )._toUint96(); // add if is reserving, subtract otherwise if (isReserving) { // We round up (by adding an extra juels) if the amount includes an // increment below REWARD_PRECISION. We always need to reserve more than // the user will earn. The consequence of this is that we’ll have dust // LINK amounts left over in the contract after stakers exit. The amount // will be approximately 1 juels for every call to reserve function, // which translates to <1 LINK for the duration of staking v0.1 contract. if (baseRewardAmount % REWARD_PRECISION > 0) deltaBaseReward++; if (delegatedRewardAmount % REWARD_PRECISION > 0) deltaDelegatedReward++; reward.reserved.base += deltaBaseReward; reward.reserved.delegated += deltaDelegatedReward; } else { reward.reserved.base -= deltaBaseReward; reward.reserved.delegated -= deltaDelegatedReward; } } /// @notice Increase reserved staker rewards. /// @param baseRewardAmount The amount of base rewards to reserve /// or unreserve for /// @param delegatedRewardAmount The amount of delegated rewards to reserve /// or unreserve for function _reserve( Reward storage reward, uint256 baseRewardAmount, uint256 delegatedRewardAmount ) internal { _updateReservedRewards( reward, baseRewardAmount, delegatedRewardAmount, true ); } /// @notice Decrease reserved staker rewards. /// @param baseRewardAmount The amount of base rewards to reserve /// or unreserve for /// @param delegatedRewardAmount The amount of delegated rewards to reserve /// or unreserve for function _unreserve( Reward storage reward, uint256 baseRewardAmount, uint256 delegatedRewardAmount ) internal { _updateReservedRewards( reward, baseRewardAmount, delegatedRewardAmount, false ); } /// @notice function does multiple things: /// - Unreserves future staking rewards to make them available for withdrawal; /// - Expires the reward to stop rewards from accumulating; function _release( Reward storage reward, uint256 amount, uint256 delegatedAmount ) internal { // Accumulate base and delegation rewards before unreserving rewards to save gas costs. // We can use the accumulated reward per micro LINK and accumulated delegation reward // to simplify reward calculations in migrate() and unstake() instead of recalculating. _accumulateDelegationRewards(reward, delegatedAmount); _accumulateBaseRewards(reward); _unreserve(reward, amount - delegatedAmount, delegatedAmount); reward.endTimestamp = block.timestamp; } /// @notice calculates an amount that community stakers have to delegate to operators /// @param amount base staked amount to calculate delegated amount against /// @param delegationRateDenominator Delegation rate used to calculate delegated stake amount function _getDelegatedAmount( uint256 amount, uint256 delegationRateDenominator ) internal pure returns (uint256) { return amount / delegationRateDenominator; } /// @notice calculates the amount of stake that remains after accounting for delegation requirement /// @param amount base staked amount to calculate non-delegated amount against /// @param delegationRateDenominator Delegation rate used to calculate delegated stake amount function _getNonDelegatedAmount( uint256 amount, uint256 delegationRateDenominator ) internal pure returns (uint256) { return amount - _getDelegatedAmount(amount, delegationRateDenominator); } /// @return uint256 the remaining reward duration (time until end), or 0 if expired/ended. function _getRemainingDuration(Reward storage reward) internal view returns (uint256) { return _isDepleted(reward) ? 0 : reward.endTimestamp - block.timestamp; } /// @notice This function is called when the staking pool is initialized, /// pool size is changed, reward rates are changed, rewards are added, and an alert is raised /// @param maxPoolSize Current maximum staking pool size /// @param totalStakedAmount Currently staked amount across community stakers and operators /// @param newRate New reward rate if it needs to be changed /// @param minRewardDuration The minimum duration rewards need to last for /// @param availableReward available reward amount /// @param totalDelegatedAmount total delegated amount delegated by community stakers function _updateDuration( Reward storage reward, uint256 maxPoolSize, uint256 totalStakedAmount, uint256 newRate, uint256 minRewardDuration, uint256 availableReward, uint256 totalDelegatedAmount ) internal { uint256 earnedBaseRewards = _getEarnedBaseRewards( reward, totalStakedAmount, totalDelegatedAmount ); uint256 earnedDelegationRewards = _getEarnedDelegationRewards( reward, totalDelegatedAmount ); uint256 remainingRewards = availableReward - earnedBaseRewards - earnedDelegationRewards; if (newRate != uint256(reward.base.rate)) { reward.base.rate = newRate._toUint80(); } uint256 availableRewardDuration = (remainingRewards * REWARD_PRECISION) / (newRate * maxPoolSize); // Validate that the new reward duration is at least the min reward duration. // This is a safety mechanism to guard against operational mistakes. if (availableRewardDuration < minRewardDuration) revert RewardDurationTooShort(); // Because we utilize unreserved rewards we need to update reserved amounts as well. // Reserved amounts are set to currently earned rewards plus new future rewards // based on the available reward duration. reward.reserved.base = (earnedBaseRewards + // Future base rewards for currently staked amounts based on the new duration _calculateReward( reward, totalStakedAmount - totalDelegatedAmount, availableRewardDuration ))._toUint96(); reward.reserved.delegated = (earnedDelegationRewards + // Future delegation rewards for currently staked amounts based on the new duration _calculateReward(reward, totalDelegatedAmount, availableRewardDuration)) ._toUint96(); reward.endTimestamp = block.timestamp + availableRewardDuration; } /// @return The total amount of base rewards earned by all stakers function _getEarnedBaseRewards( Reward storage reward, uint256 totalStakedAmount, uint256 totalDelegatedAmount ) internal view returns (uint256) { return reward.reserved.base - _calculateReward( reward, totalStakedAmount - totalDelegatedAmount, _getRemainingDuration(reward) ); } /// @return The total amount of delegated rewards earned by all node operators function _getEarnedDelegationRewards( Reward storage reward, uint256 totalDelegatedAmount ) internal view returns (uint256) { return reward.reserved.delegated - _calculateReward( reward, totalDelegatedAmount, _getRemainingDuration(reward) ); } /// @notice Slashes all on feed node operators. /// Node operators are slashed the minimum of either the /// amount of rewards they have earned or the amount /// of rewards earned by the minimum operator stake amount /// over the slashable duration. function _slashOnFeedOperators( Reward storage reward, uint256 minOperatorStakeAmount, uint256 slashableDuration, address[] memory feedOperators, mapping(address => StakingPoolLib.Staker) storage stakers, uint256 totalDelegatedAmount ) internal { if (reward.delegated.delegatesCount == 0) return; // Skip slashing if there are no staking operators uint256 slashableBaseRewards = _getSlashableBaseRewards( reward, minOperatorStakeAmount, slashableDuration ); uint256 slashableDelegatedRewards = _getSlashableDelegatedRewards( reward, slashableDuration, totalDelegatedAmount ); uint256 totalSlashedBaseReward; uint256 totalSlashedDelegatedReward; uint256[] memory slashedBaseAmounts = new uint256[](feedOperators.length); uint256[] memory slashedDelegatedAmounts = new uint256[]( feedOperators.length ); for (uint256 i; i < feedOperators.length; i++) { address operator = feedOperators[i]; uint256 operatorStakedAmount = stakers[operator].stakedAmount; if (operatorStakedAmount == 0) continue; slashedBaseAmounts[i] = _slashOperatorBaseRewards( reward, slashableBaseRewards, operator, operatorStakedAmount ); slashedDelegatedAmounts[i] = _slashOperatorDelegatedRewards( reward, slashableDelegatedRewards, operator, totalDelegatedAmount ); totalSlashedBaseReward += slashedBaseAmounts[i]; totalSlashedDelegatedReward += slashedDelegatedAmounts[i]; } reward.reserved.base -= totalSlashedBaseReward._toUint96(); reward.reserved.delegated -= totalSlashedDelegatedReward._toUint96(); emit RewardSlashed( feedOperators, slashedBaseAmounts, slashedDelegatedAmounts ); } /// @return The amount of base rewards to slash /// @notice The amount of rewards accrued over the slashable duration for a /// minimum node operator stake amount function _getSlashableBaseRewards( Reward storage reward, uint256 minOperatorStakeAmount, uint256 slashableDuration ) private view returns (uint256) { return _calculateReward(reward, minOperatorStakeAmount, slashableDuration); } /// @return The amount of delegated rewards to slash /// @dev The amount of delegated rewards accrued over the slashable duration function _getSlashableDelegatedRewards( Reward storage reward, uint256 slashableDuration, uint256 totalDelegatedAmount ) private view returns (uint256) { DelegatedRewards memory delegatedRewards = reward.delegated; return _calculateReward(reward, totalDelegatedAmount, slashableDuration) / // We don't validate for delegatedRewards.delegatesCount to be a // non-zero value as this is already checked in _slashOnFeedOperators. uint256(delegatedRewards.delegatesCount); } /// @notice Slashes an on feed node operator the minimum of /// either the total amount of base rewards they have /// earned or the amount of rewards earned by the /// minimum operator stake amount over the slashable duration. function _slashOperatorBaseRewards( Reward storage reward, uint256 slashableRewards, address operator, uint256 operatorStakedAmount ) private returns (uint256) { uint256 earnedRewards = _getOperatorEarnedBaseRewards( reward, operator, operatorStakedAmount ); uint256 slashedRewards = Math.min(slashableRewards, earnedRewards); // max capped by earnings reward.missed[operator].base += slashedRewards._toUint96(); return slashedRewards; } /// @notice Slashes an on feed node operator the minimum of /// either the total amount of delegated rewards they have /// earned or the amount of delegated rewards they have /// earned over the slashable duration. function _slashOperatorDelegatedRewards( Reward storage reward, uint256 slashableRewards, address operator, uint256 totalDelegatedAmount ) private returns (uint256) { uint256 earnedRewards = _getOperatorEarnedDelegatedRewards( reward, operator, totalDelegatedAmount ); uint256 slashedRewards = Math.min(slashableRewards, earnedRewards); // max capped by earnings reward.missed[operator].delegated += slashedRewards._toUint96(); return slashedRewards; } /// @return The amount of base rewards an operator /// has earned. function _getOperatorEarnedBaseRewards( Reward storage reward, address operator, uint256 operatorStakedAmount ) internal view returns (uint256) { return _calculateAccruedBaseRewards(reward, operatorStakedAmount) - uint256(reward.missed[operator].base); } /// @return The amount of delegated rewards an operator /// has earned. function _getOperatorEarnedDelegatedRewards( Reward storage reward, address operator, uint256 totalDelegatedAmount ) internal view returns (uint256) { return _calculateAccruedDelegatedRewards(reward, totalDelegatedAmount) - uint256(reward.missed[operator].delegated); } /// @return The current timestamp or, if the current timestamp has passed reward /// end timestamp, reward end timestamp. /// @dev This is necessary to ensure that rewards are calculated correctly /// after the reward is depleted. function _getCappedTimestamp(Reward storage reward) internal view returns (uint256) { return Math.min(uint256(reward.endTimestamp), block.timestamp); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; /// @notice Owner functions restricted to the setup and maintenance /// of the staking contract by the owner. interface IStakingOwner { /// @notice This error is thrown when an zero delegation rate is supplied error InvalidDelegationRate(); /// @notice This error is thrown when an invalid regular period threshold is supplied error InvalidRegularPeriodThreshold(); /// @notice This error is thrown when an invalid min operator stake amount is /// supplied error InvalidMinOperatorStakeAmount(); /// @notice This error is thrown when an invalid min community stake amount /// is supplied error InvalidMinCommunityStakeAmount(); /// @notice This error is thrown when an invalid max alerting reward is /// supplied error InvalidMaxAlertingRewardAmount(); /// @notice This error is thrown when the pool is started with an empty /// merkle root error MerkleRootNotSet(); /// @notice Adds one or more operators to a list of operators /// @dev Should only callable by the Owner /// @param operators A list of operator addresses to add function addOperators(address[] calldata operators) external; /// @notice Removes one or more operators from a list of operators. When an /// operator is removed, we store their principal in a separate mapping to /// prevent immediate withdrawals. This is so that the removed operator can /// only unstake at the same time as every other staker. /// @dev Should only be callable by the owner when the pool is open. /// When an operator is removed they can stake as a community staker. /// We allow that because the alternative (checking for removed stake before /// staking) is going to unnecessarily increase gas costs in 99.99% of the /// cases. /// @param operators A list of operator addresses to remove function removeOperators(address[] calldata operators) external; /// @notice Allows the contract owner to set the list of on-feed operator addresses who are subject to slashing /// @dev Existing feed operators are cleared before setting the new operators. /// @param operators New list of on-feed operator staker addresses function setFeedOperators(address[] calldata operators) external; /// @return List of the ETH-USD feed node operators' staking addresses function getFeedOperators() external view returns (address[] memory); /// @notice This function can be called to change the reward rate for the pool. /// This change only affects future rewards, i.e. rewards earned at a previous /// rate are unaffected. /// @dev Should only be callable by the owner. The rate can be increased or decreased. /// The new rate cannot be 0. /// @param rate The new reward rate function changeRewardRate(uint256 rate) external; /// @notice This function can be called to add rewards to the pool /// @dev Should only be callable by the owner /// @param amount The amount of rewards to add to the pool function addReward(uint256 amount) external; /// @notice This function can be called to withdraw unused reward amount from /// the staking pool. It can be called before the pool is initialized, after /// the pool is concluded or when the reward expires. /// @dev Should only be callable by the owner when the pool is closed function withdrawUnusedReward() external; /// @notice Set the pool config /// @param maxPoolSize The max amount of staked LINK allowed in the pool /// @param maxCommunityStakeAmount The max amount of LINK a community staker can stake /// @param maxOperatorStakeAmount The max amount of LINK a Node Op can stake function setPoolConfig( uint256 maxPoolSize, uint256 maxCommunityStakeAmount, uint256 maxOperatorStakeAmount ) external; /// @notice Transfers LINK tokens and initializes the reward /// @dev Uses ERC20 approve + transferFrom flow /// @param amount rewards amount in LINK /// @param initialRewardRate The amount of LINK earned per second for /// each LINK staked. function start(uint256 amount, uint256 initialRewardRate) external; /// @notice Closes the pool, unreserving future staker rewards, expires the /// reward and releases unreserved rewards function conclude() external; /// @notice This function pauses staking /// @dev Sets the pause flag to true function emergencyPause() external; /// @notice This function unpauses staking /// @dev Sets the pause flag to false function emergencyUnpause() external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import {SafeCast} from './SafeCast.sol'; library StakingPoolLib { using SafeCast for uint256; /// @notice This event is emitted when the staking pool is opened for stakers event PoolOpened(); /// @notice This event is emitted when the staking pool is concluded event PoolConcluded(); /// @notice This event is emitted when the staking pool's maximum size is /// increased /// @param maxPoolSize the new maximum pool size event PoolSizeIncreased(uint256 maxPoolSize); /// @notice This event is emitted when the maximum stake amount // for community stakers is increased /// @param maxStakeAmount the new maximum stake amount event MaxCommunityStakeAmountIncreased(uint256 maxStakeAmount); /// @notice This event is emitted when the maximum stake amount for node /// operators is increased /// @param maxStakeAmount the new maximum stake amount event MaxOperatorStakeAmountIncreased(uint256 maxStakeAmount); /// @notice This event is emitted when an operator is added /// @param operator address of the operator that was added to the staking pool event OperatorAdded(address operator); /// @notice This event is emitted when an operator is removed /// @param operator address of the operator that was removed from the staking pool /// @param amount principal amount that will be available for withdrawal when staking ends event OperatorRemoved(address operator, uint256 amount); /// @notice This event is emitted when the contract owner sets the list /// of feed operators subject to slashing /// @param feedOperators new list of feed operator staking addresses event FeedOperatorsSet(address[] feedOperators); /// @notice Surfaces the required pool status to perform an operation /// @param currentStatus current status of the pool (true if open / false if closed) /// @param requiredStatus required status of the pool to proceed /// (true if pool must be open / false if pool must be closed) error InvalidPoolStatus(bool currentStatus, bool requiredStatus); /// @notice This error is raised when attempting to decrease maximum pool size /// @param maxPoolSize the current maximum pool size error InvalidPoolSize(uint256 maxPoolSize); /// @notice This error is raised when attempting to decrease maximum stake amount /// for community stakers or node operators /// @param maxStakeAmount the current maximum stake amount error InvalidMaxStakeAmount(uint256 maxStakeAmount); /// @notice This error is raised when attempting to add more node operators without /// sufficient available pool space to reserve their allocations. /// @param remainingPoolSize the remaining pool space available to reserve /// @param requiredPoolSize the required reserved pool space to add new node operators error InsufficientRemainingPoolSpace( uint256 remainingPoolSize, uint256 requiredPoolSize ); /// @param requiredAmount minimum required stake amount error InsufficientStakeAmount(uint256 requiredAmount); /// @notice This error is raised when stakers attempt to stake past pool limits /// @param remainingAmount maximum remaining amount that can be staked. This is /// the difference between the existing staked amount and the individual and global limits. error ExcessiveStakeAmount(uint256 remainingAmount); /// @notice This error is raised when stakers attempt to exit the pool /// @param staker address of the staker who attempted to withdraw funds error StakeNotFound(address staker); /// @notice This error is raised when addresses with existing stake is added as an operator /// @param staker address of the staker who is being added as an operator error ExistingStakeFound(address staker); /// @notice This error is raised when an address is duplicated in the supplied list of operators. /// This can happen in addOperators and setFeedOperators functions. /// @param operator address of the operator error OperatorAlreadyExists(address operator); /// @notice This error is thrown when the owner attempts to remove an on-feed operator. /// @dev The owner must remove the operator from the on-feed list first. error OperatorIsAssignedToFeed(address operator); /// @notice This error is raised when removing an operator in removeOperators /// and setFeedOperators /// @param operator address of the operator error OperatorDoesNotExist(address operator); /// @notice This error is raised when operator has been removed from the pool /// and is attempted to be readded /// @param operator address of the locked operator error OperatorIsLocked(address operator); /// @notice This error is raised when attempting to start staking with less /// than the minimum required node operators /// @param currentOperatorsCount The current number of operators in the staking pool /// @param minInitialOperatorsCount The minimum required number of operators /// in the staking pool before opening error InadequateInitialOperatorsCount( uint256 currentOperatorsCount, uint256 minInitialOperatorsCount ); struct PoolLimits { // The max amount of staked LINK allowed in the pool uint96 maxPoolSize; // The max amount of LINK a community staker can stake uint80 maxCommunityStakeAmount; // The max amount of LINK a Node Op can stake uint80 maxOperatorStakeAmount; } struct PoolState { // Flag that signals if the staking pool is open for staking bool isOpen; // Total number of operators added to the staking pool uint8 operatorsCount; // Total amount of LINK staked by community stakers uint96 totalCommunityStakedAmount; // Total amount of LINK staked by operators uint96 totalOperatorStakedAmount; } struct Staker { // Flag that signals whether a staker is an operator bool isOperator; // Flag that signals whether a staker is an on-feed operator bool isFeedOperator; // Amount of LINK staked by a staker uint96 stakedAmount; // Amount of LINK staked by a removed operator that can be withdrawn // Removed operators can only withdraw at the end of staking. // Used to know which operators have been removed. uint96 removedStakeAmount; } struct Pool { mapping(address => Staker) stakers; address[] feedOperators; PoolState state; PoolLimits limits; // Sum of removed operator principals that have not been withdrawn. // Used to make sure that contract's balance is correct. // total staked amount + total removed amount + available rewards = current balance uint256 totalOperatorRemovedAmount; } /// @notice Sets staking pool parameters /// @param maxPoolSize Maximum total stake amount across all stakers /// @param maxCommunityStakeAmount Maximum stake amount for a single community staker /// @param maxOperatorStakeAmount Maximum stake amount for a single node operator function _setConfig( Pool storage pool, uint256 maxPoolSize, uint256 maxCommunityStakeAmount, uint256 maxOperatorStakeAmount ) internal { if (maxOperatorStakeAmount > maxPoolSize) revert InvalidMaxStakeAmount(maxOperatorStakeAmount); if (pool.limits.maxPoolSize > maxPoolSize) revert InvalidPoolSize(maxPoolSize); if (pool.limits.maxCommunityStakeAmount > maxCommunityStakeAmount) revert InvalidMaxStakeAmount(maxCommunityStakeAmount); if (pool.limits.maxOperatorStakeAmount > maxOperatorStakeAmount) revert InvalidMaxStakeAmount(maxOperatorStakeAmount); PoolState memory poolState = pool.state; if ( maxPoolSize < (poolState.operatorsCount * maxOperatorStakeAmount) + poolState.totalCommunityStakedAmount ) revert InvalidMaxStakeAmount(maxOperatorStakeAmount); if (pool.limits.maxPoolSize != maxPoolSize) { pool.limits.maxPoolSize = maxPoolSize._toUint96(); emit PoolSizeIncreased(maxPoolSize); } if (pool.limits.maxCommunityStakeAmount != maxCommunityStakeAmount) { pool.limits.maxCommunityStakeAmount = maxCommunityStakeAmount._toUint80(); emit MaxCommunityStakeAmountIncreased(maxCommunityStakeAmount); } if (pool.limits.maxOperatorStakeAmount != maxOperatorStakeAmount) { pool.limits.maxOperatorStakeAmount = maxOperatorStakeAmount._toUint80(); emit MaxOperatorStakeAmountIncreased(maxOperatorStakeAmount); } } /// @notice Opens the staking pool function _open(Pool storage pool, uint256 minInitialOperatorCount) internal { if (uint256(pool.state.operatorsCount) < minInitialOperatorCount) revert InadequateInitialOperatorsCount( pool.state.operatorsCount, minInitialOperatorCount ); pool.state.isOpen = true; emit PoolOpened(); } /// @notice Closes the staking pool function _close(Pool storage pool) internal { pool.state.isOpen = false; emit PoolConcluded(); } /// @notice Returns true if a supplied staker address is in the operators list /// @param staker Address of a staker /// @return bool function _isOperator(Pool storage pool, address staker) internal view returns (bool) { return pool.stakers[staker].isOperator; } /// @notice Returns the sum of all principal staked in the pool /// @return totalStakedAmount function _getTotalStakedAmount(Pool storage pool) internal view returns (uint256) { StakingPoolLib.PoolState memory poolState = pool.state; return uint256(poolState.totalCommunityStakedAmount) + uint256(poolState.totalOperatorStakedAmount); } /// @notice Returns the amount of remaining space available in the pool for /// community stakers. Community stakers can only stake up to this amount /// even if they are within their individual limits. /// @return remainingPoolSpace function _getRemainingPoolSpace(Pool storage pool) internal view returns (uint256) { StakingPoolLib.PoolState memory poolState = pool.state; return uint256(pool.limits.maxPoolSize) - (uint256(poolState.operatorsCount) * uint256(pool.limits.maxOperatorStakeAmount)) - uint256(poolState.totalCommunityStakedAmount); } /// @dev Required conditions for adding operators: /// - Operators can only been added to the pool if they have no prior stake. /// - Operators can only been readded to the pool if they have no removed /// stake. /// - Operators cannot be added to the pool after staking ends (either through /// conclusion or through reward expiry). function _addOperators(Pool storage pool, address[] calldata operators) internal { uint256 requiredReservedPoolSpace = operators.length * uint256(pool.limits.maxOperatorStakeAmount); uint256 remainingPoolSpace = _getRemainingPoolSpace(pool); if (requiredReservedPoolSpace > remainingPoolSpace) revert InsufficientRemainingPoolSpace( remainingPoolSpace, requiredReservedPoolSpace ); for (uint256 i; i < operators.length; i++) { if (pool.stakers[operators[i]].isOperator) revert OperatorAlreadyExists(operators[i]); if (pool.stakers[operators[i]].stakedAmount > 0) revert ExistingStakeFound(operators[i]); // Avoid edge-cases where we attempt to add an operator that has // locked principal (this means that the operator was previously removed). if (pool.stakers[operators[i]].removedStakeAmount > 0) revert OperatorIsLocked(operators[i]); pool.stakers[operators[i]].isOperator = true; emit OperatorAdded(operators[i]); } // Safely update operators count with respect to the maximum of 255 operators pool.state.operatorsCount = pool.state.operatorsCount + operators.length._toUint8(); } /// @notice Helper function to set the list of on-feed Operator addresses /// @param operators List of Operator addresses function _setFeedOperators(Pool storage pool, address[] calldata operators) internal { for (uint256 i; i < pool.feedOperators.length; i++) { delete pool.stakers[pool.feedOperators[i]].isFeedOperator; } delete pool.feedOperators; for (uint256 i; i < operators.length; i++) { address newFeedOperator = operators[i]; if (!_isOperator(pool, newFeedOperator)) revert OperatorDoesNotExist(newFeedOperator); if (pool.stakers[newFeedOperator].isFeedOperator) revert OperatorAlreadyExists(newFeedOperator); pool.stakers[newFeedOperator].isFeedOperator = true; } pool.feedOperators = operators; emit FeedOperatorsSet(operators); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; interface IAlertsController { /// @param alerter The address of an alerter /// @param roundId The feed's round ID that an alert has been raised for /// @param rewardAmount The amount of LINK rewarded to the alerter /// @notice Emitted when a valid alert is raised for a feed round event AlertRaised(address alerter, uint256 roundId, uint256 rewardAmount); /// @param roundId The feed's round ID that the alerter is trying to raise an alert for /// @notice This error is thrown when an alerter tries to raise an // alert for a round that has already been alerted. error AlertAlreadyExists(uint256 roundId); /// @notice This error is thrown when alerting conditions are not met and the /// alert is invalid. error AlertInvalid(); /// @notice This function creates an alert for a stalled feed function raiseAlert() external; /// @notice This function checks to see whether the alerter may raise an alert /// to claim rewards function canAlert(address alerter) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; interface IMigratable { /// @notice This event is emitted when a migration target is proposed by the contract owner. /// @param migrationTarget Contract address to migrate stakes to. event MigrationTargetProposed(address migrationTarget); /// @notice This event is emitted after a 7 day period has passed since a migration target is proposed, and the target is accepted. /// @param migrationTarget Contract address to migrate stakes to. event MigrationTargetAccepted(address migrationTarget); /// @notice This event is emitted when a staker migrates their stake to the migration target. /// @param staker Staker address /// @param principal Principal amount deposited /// @param baseReward Amount of base rewards withdrawn /// @param delegationReward Amount of delegation rewards withdrawn (if applicable) /// @param data Migration payload event Migrated( address staker, uint256 principal, uint256 baseReward, uint256 delegationReward, bytes data ); /// @notice This error is raised when the contract owner supplies a non-contract migration target. error InvalidMigrationTarget(); /// @notice This function returns the migration target contract address function getMigrationTarget() external view returns (address); /// @notice This function allows the contract owner to set a proposed /// migration target address. If the migration target is valid it renounces /// the previously accepted migration target (if any). /// @param migrationTarget Contract address to migrate stakes to. function proposeMigrationTarget(address migrationTarget) external; /// @notice This function allows the contract owner to accept a proposed migration target address after a waiting period. function acceptMigrationTarget() external; /// @notice This function allows stakers to migrate funds to a new staking pool. /// @param data Migration path details function migrate(bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; interface IStaking { /// @notice This event is emitted when a staker adds stake to the pool. /// @param staker Staker address /// @param newStake New principal amount staked /// @param totalStake Total principal amount staked event Staked(address staker, uint256 newStake, uint256 totalStake); /// @notice This event is emitted when a staker exits the pool. /// @param staker Staker address /// @param principal Principal amount staked /// @param baseReward base reward earned /// @param delegationReward delegation reward earned, if any event Unstaked( address staker, uint256 principal, uint256 baseReward, uint256 delegationReward ); /// @notice This error is thrown whenever the sender is not the LINK token error SenderNotLinkToken(); /// @notice This error is thrown whenever an address does not have access /// to successfully execute a transaction error AccessForbidden(); /// @notice This error is thrown whenever a zero-address is supplied when /// a non-zero address is required error InvalidZeroAddress(); /// @notice This function allows stakers to exit the pool after it has been /// concluded. It returns the principal as well as base and delegation /// rewards. function unstake() external; /// @notice This function allows removed operators to withdraw their original /// principal. Operators can only withdraw after the pool is closed, like /// every other staker. function withdrawRemovedStake() external; /// @return address LINK token contract's address that is used by the pool function getChainlinkToken() external view returns (address); /// @param staker address /// @return uint256 staker's staked principal amount function getStake(address staker) external view returns (uint256); /// @notice Returns true if an address is an operator function isOperator(address staker) external view returns (bool); /// @notice The staking pool starts closed and only allows /// stakers to stake once it's opened /// @return bool pool status function isActive() external view returns (bool); /// @return uint256 current maximum staking pool size function getMaxPoolSize() external view returns (uint256); /// @return uint256 minimum amount that can be staked by a community staker /// @return uint256 maximum amount that can be staked by a community staker function getCommunityStakerLimits() external view returns (uint256, uint256); /// @return uint256 minimum amount that can be staked by an operator /// @return uint256 maximum amount that can be staked by an operator function getOperatorLimits() external view returns (uint256, uint256); /// @return uint256 reward initialization timestamp /// @return uint256 reward expiry timestamp function getRewardTimestamps() external view returns (uint256, uint256); /// @return uint256 current reward rate, expressed in juels per second per micro LINK function getRewardRate() external view returns (uint256); /// @return uint256 current delegation rate function getDelegationRateDenominator() external view returns (uint256); /// @return uint256 total amount of LINK tokens made available for rewards in /// Juels /// @dev This reflects how many rewards were made available over the /// lifetime of the staking pool. This is not updated when the rewards are /// unstaked or migrated by the stakers. This means that the contract balance /// will dip below available amount when the reward expires and users start /// moving their rewards. function getAvailableReward() external view returns (uint256); /// @return uint256 amount of base rewards earned by a staker in Juels function getBaseReward(address) external view returns (uint256); /// @return uint256 amount of delegation rewards earned by an operator in Juels function getDelegationReward(address) external view returns (uint256); /// @notice Total delegated amount is calculated by dividing the total /// community staker staked amount by the delegation rate, i.e. /// totalDelegatedAmount = pool.totalCommunityStakedAmount / delegationRateDenominator /// @return uint256 staked amount that is used when calculating delegation rewards in Juels function getTotalDelegatedAmount() external view returns (uint256); /// @notice Delegates count increases after an operator is added to the list /// of operators and stakes the minimum required amount. /// @return uint256 number of staking operators that are eligible for delegation rewards function getDelegatesCount() external view returns (uint256); /// @return uint256 total amount of base rewards earned by all stakers in Juels function getEarnedBaseRewards() external view returns (uint256); /// @return uint256 total amount of delegated rewards earned by all node operators in Juels function getEarnedDelegationRewards() external view returns (uint256); /// @return uint256 total amount staked by community stakers and operators in Juels function getTotalStakedAmount() external view returns (uint256); /// @return uint256 total amount staked by community stakers in Juels function getTotalCommunityStakedAmount() external view returns (uint256); /// @return uint256 the sum of removed operator principals that have not been /// withdrawn from the staking pool in Juels. /// @dev Used to make sure that contract's balance is correct. /// total staked amount + total removed amount + available rewards = current balance function getTotalRemovedAmount() external view returns (uint256); /// @notice This function returns the pause state /// @return bool whether or not the pool is paused function isPaused() external view returns (bool); /// @return address The address of the feed being monitored to raise alerts for function getMonitoredFeed() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; interface IMerkleAccessController { /// @notice Emitted when the contract owner updates the staking allowlist /// @param newMerkleRoot The root of a new Staking allowlist merkle tree event MerkleRootChanged(bytes32 newMerkleRoot); /// @notice Validates if a community staker has access to the private staking pool /// @param staker The community staker's address /// @param proof Merkle proof for the community staker's allowlist function hasAccess(address staker, bytes32[] calldata proof) external view returns (bool); /// @notice This function is called to update the staking allowlist in a private staking pool /// @dev Only callable by the contract owner /// @param newMerkleRoot Merkle Tree root, used to prove access for community stakers /// will be required at start but can be removed at any time by the owner when /// staking access will be granted to the public. function setMerkleRoot(bytes32 newMerkleRoot) external; /// @return The current root of the Staking allowlist merkle tree function getMerkleRoot() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; abstract contract TypeAndVersionInterface { function typeAndVersion() external pure virtual returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./ConfirmedOwnerWithProposal.sol"; /** * @title The ConfirmedOwner contract * @notice A contract with helpers for basic contract ownership. */ contract ConfirmedOwner is ConfirmedOwnerWithProposal { constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Trees proofs. * * The proofs can be generated using the JavaScript library * https://github.com/miguelmota/merkletreejs[merkletreejs]. * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. * * See `test/utils/cryptography/MerkleProof.test.js` for some examples. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = _efficientHash(computedHash, proofElement); } else { // Hash(current element of the proof + current computed hash) computedHash = _efficientHash(proofElement, computedHash); } } return computedHash; } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; library SafeCast { error CastError(); /// @notice This is used to safely case timestamps to uint8 uint256 private constant MAX_UINT_8 = type(uint8).max; /// @notice This is used to safely case timestamps to uint32 uint256 private constant MAX_UINT_32 = type(uint32).max; /// @notice This is used to safely case timestamps to uint80 uint256 private constant MAX_UINT_80 = type(uint80).max; /// @notice This is used to safely case timestamps to uint96 uint256 private constant MAX_UINT_96 = type(uint96).max; function _toUint8(uint256 value) internal pure returns (uint8) { if (value > MAX_UINT_8) revert CastError(); return uint8(value); } function _toUint32(uint256 value) internal pure returns (uint32) { if (value > MAX_UINT_32) revert CastError(); return uint32(value); } function _toUint80(uint256 value) internal pure returns (uint80) { if (value > MAX_UINT_80) revert CastError(); return uint80(value); } function _toUint96(uint256 value) internal pure returns (uint96) { if (value > MAX_UINT_96) revert CastError(); return uint96(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/OwnableInterface.sol"; /** * @title The ConfirmedOwner contract * @notice A contract with helpers for basic contract ownership. */ contract ConfirmedOwnerWithProposal is OwnableInterface { address private s_owner; address private s_pendingOwner; event OwnershipTransferRequested(address indexed from, address indexed to); event OwnershipTransferred(address indexed from, address indexed to); constructor(address newOwner, address pendingOwner) { require(newOwner != address(0), "Cannot set owner to zero"); s_owner = newOwner; if (pendingOwner != address(0)) { _transferOwnership(pendingOwner); } } /** * @notice Allows an owner to begin transferring ownership to a new address, * pending. */ function transferOwnership(address to) public override onlyOwner { _transferOwnership(to); } /** * @notice Allows an ownership transfer to be completed by the recipient. */ function acceptOwnership() external override { require(msg.sender == s_pendingOwner, "Must be proposed owner"); address oldOwner = s_owner; s_owner = msg.sender; s_pendingOwner = address(0); emit OwnershipTransferred(oldOwner, msg.sender); } /** * @notice Get the current owner */ function owner() public view override returns (address) { return s_owner; } /** * @notice validate, transfer ownership, and emit relevant events */ function _transferOwnership(address to) private { require(to != msg.sender, "Cannot transfer to self"); s_pendingOwner = to; emit OwnershipTransferRequested(s_owner, to); } /** * @notice validate access */ function _validateOwnership() internal view { require(msg.sender == s_owner, "Only callable by owner"); } /** * @notice Reverts if called by anyone other than the contract owner. */ modifier onlyOwner() { _validateOwnership(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface OwnableInterface { function owner() external returns (address); function transferOwnership(address recipient) external; function acceptOwnership() external; }
// 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); }
{ "optimizer": { "enabled": true, "runs": 75000 }, "viaIR": true, "metadata": { "bytecodeHash": "none", "useLiteralContent": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"components":[{"internalType":"contract LinkTokenInterface","name":"LINKAddress","type":"address"},{"internalType":"contract AggregatorV3Interface","name":"monitoredFeed","type":"address"},{"internalType":"uint256","name":"initialMaxPoolSize","type":"uint256"},{"internalType":"uint256","name":"initialMaxCommunityStakeAmount","type":"uint256"},{"internalType":"uint256","name":"initialMaxOperatorStakeAmount","type":"uint256"},{"internalType":"uint256","name":"minCommunityStakeAmount","type":"uint256"},{"internalType":"uint256","name":"minOperatorStakeAmount","type":"uint256"},{"internalType":"uint256","name":"priorityPeriodThreshold","type":"uint256"},{"internalType":"uint256","name":"regularPeriodThreshold","type":"uint256"},{"internalType":"uint256","name":"maxAlertingRewardAmount","type":"uint256"},{"internalType":"uint256","name":"minInitialOperatorCount","type":"uint256"},{"internalType":"uint256","name":"minRewardDuration","type":"uint256"},{"internalType":"uint256","name":"slashableDuration","type":"uint256"},{"internalType":"uint256","name":"delegationRateDenominator","type":"uint256"}],"internalType":"struct Staking.PoolConstructorParams","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessForbidden","type":"error"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"AlertAlreadyExists","type":"error"},{"inputs":[],"name":"AlertInvalid","type":"error"},{"inputs":[],"name":"CastError","type":"error"},{"inputs":[{"internalType":"uint256","name":"remainingAmount","type":"uint256"}],"name":"ExcessiveStakeAmount","type":"error"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"ExistingStakeFound","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentOperatorsCount","type":"uint256"},{"internalType":"uint256","name":"minInitialOperatorsCount","type":"uint256"}],"name":"InadequateInitialOperatorsCount","type":"error"},{"inputs":[{"internalType":"uint256","name":"remainingPoolSize","type":"uint256"},{"internalType":"uint256","name":"requiredPoolSize","type":"uint256"}],"name":"InsufficientRemainingPoolSpace","type":"error"},{"inputs":[{"internalType":"uint256","name":"requiredAmount","type":"uint256"}],"name":"InsufficientStakeAmount","type":"error"},{"inputs":[],"name":"InvalidDelegationRate","type":"error"},{"inputs":[],"name":"InvalidMaxAlertingRewardAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxStakeAmount","type":"uint256"}],"name":"InvalidMaxStakeAmount","type":"error"},{"inputs":[],"name":"InvalidMigrationTarget","type":"error"},{"inputs":[],"name":"InvalidMinCommunityStakeAmount","type":"error"},{"inputs":[],"name":"InvalidMinOperatorStakeAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxPoolSize","type":"uint256"}],"name":"InvalidPoolSize","type":"error"},{"inputs":[{"internalType":"bool","name":"currentStatus","type":"bool"},{"internalType":"bool","name":"requiredStatus","type":"bool"}],"name":"InvalidPoolStatus","type":"error"},{"inputs":[],"name":"InvalidRegularPeriodThreshold","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"MerkleRootNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorAlreadyExists","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorDoesNotExist","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorIsAssignedToFeed","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorIsLocked","type":"error"},{"inputs":[],"name":"RewardDurationTooShort","type":"error"},{"inputs":[],"name":"SenderNotLinkToken","type":"error"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"StakeNotFound","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"alerter","type":"address"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"name":"AlertRaised","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"newMerkleRoot","type":"bytes32"}],"name":"MerkleRootChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegationReward","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Migrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"migrationTarget","type":"address"}],"name":"MigrationTargetAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"migrationTarget","type":"address"}],"name":"MigrationTargetProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"newStake","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalStake","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegationReward","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[],"name":"acceptMigrationTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"addOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"alerter","type":"address"}],"name":"canAlert","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newRate","type":"uint256"}],"name":"changeRewardRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"conclude","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyUnpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAvailableReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getBaseReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainlinkToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCommunityStakerLimits","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDelegatesCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDelegationRateDenominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getDelegationReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEarnedBaseRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEarnedDelegationRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeedOperators","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxPoolSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMigrationTarget","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMonitoredFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOperatorLimits","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTimestamps","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalCommunityStakedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalDelegatedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalRemovedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalStakedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"hasAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"migrationTarget","type":"address"}],"name":"proposeMigrationTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"raiseAlert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"removeOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"setFeedOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newMerkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxPoolSize","type":"uint256"},{"internalType":"uint256","name":"maxCommunityStakeAmount","type":"uint256"},{"internalType":"uint256","name":"maxOperatorStakeAmount","type":"uint256"}],"name":"setPoolConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"initialRewardRate","type":"uint256"}],"name":"start","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawRemovedStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawUnusedReward","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
34620006c557620063ff388190036101e0601f8201601f19168101906001600160401b03821190821017620005b7576101c09282916040526101e03912620006c5576040516101c081016001600160401b03811182821017620005b7576040526101e0516001600160a01b0381168103620006c5578152610200516001600160a01b0381168103620006c55760208201526102205160408201526102405160608201526102605160808201526102805160a08201526102a05160c08201526102c05160e08201526102e05161010082015261030051610120820152610320516101408201526103405161016082015261036051610180820152610380516101a082015233156200068057600080546001600160a01b031916331790556001805460ff60a01b1916905580516001600160a01b0316156200066e5760208101516001600160a01b0316156200066e576101a08101511562000646576101a08101518015620006585764e8d4a5100006620006465761010081015160e08201511015620006345760c081015115620006225760c0810151608082015110620006225760a08101516060820151106200061057610120810151608082015110620005fe576040810151906060810151608082015190838211620005cd57600554936001600160601b0385168110620005e657606085901c6001600160501b03168210620005cd57828560b01c1162000588576040516001600160401b036080820190811190821117620005b757608081016040526004549060ff82161515815260ff8260081c169081602082015260606040820193600180831b038160101c168552600180831b039060701c169101528060001904851181151516620005a15790516001600160601b0316908402908101908110620005a157811062000588576001600160601b03851681900362000516575b506005546101a09450606081901c6001600160501b0316829003620004b4575b5050600554818160b01c036200045b575b505060018060a01b0381511660805260018060a01b0360208201511660a05260e081015160c05261010081015160e0526101208101516101005260c08101516101205260a0810151610140526101408101516101605261016081015161018052610180810151825201516101c052604051615d1f9081620006e082396080518181816104eb01528181610798015281816110700152818161169c01528181611ba801528181612c2501528181614b690152614e05015260a05181818161148401528181612ad701526149f1015260c051818181612b260152614a40015260e051818181612b570152614a7101526101005181818161597801526159a10152610120518181816114eb01528181612c8a0152615455015261014051818181610451015261510a01526101605181611b3e015261018051818181611116015281816112070152611c1301526101a05181612c6901526101c051818181610c3e01528181614c7001528181614dc30152818161519b015261584b0152f35b7f816587cb2e773af4f3689a03d7520fabff3462605ded374b485b13994c0d7b52916020916001600160b01b03196200049483620006ca565b60b01b169060018060b01b031617600555604051908152a13880620002e0565b7fb5f554e5ef00806bace1edbb84186512ebcefa2af7706085143f501f29314df791602091600160601b600160b01b03620004ef83620006ca565b60601b16600160601b600160b01b03199190911617600555604051908152a13880620002cf565b6001600160601b03811162000576576001600160601b03199094166001600160601b038516176005556040519384526101a0937f7f4f497e086b2eb55f8a9885ba00d33399bbe0ebcb92ea092834386435a1b9c090602090a138620002af565b60405163408ba96f60e11b8152600490fd5b60405163bc91aa3360e01b815260048101849052602490fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b60405163bc91aa3360e01b815260048101839052602490fd5b60249060405190630f9e1c3b60e11b82526004820152fd5b60405163a9be3a0960e01b8152600490fd5b60405163941b857f60e01b8152600490fd5b6040516384dada8560e01b8152600490fd5b6040516310919fb960e11b8152600490fd5b60405163027953ef60e61b8152600490fd5b634e487b7160e01b600052601260045260246000fd5b60405163f6b2911f60e01b8152600490fd5b60405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f00000000000000006044820152606490fd5b600080fd5b6001600160501b03908181116200057657169056fe60806040526004361015610013575b600080fd5b60003560e01c8063049b2ca0146104065780630641bdd8146103fd5780630fbc8f5b146103f4578063165d35e1146103eb578063181f5a77146103e25780631a9d4c7c146103d95780631ddb5552146103d057806322f3e2d4146103c75780632def6620146103be57806332e28850146103b557806338adb6f0146103ac57806349590657146103a35780634a4e3bd51461039a57806351858e271461039157806359f01879146103885780635aa6e0131461037f5780635c975abb1461029e5780635e8b40d7146103765780635fec60f81461036d57806363b2c85a146103645780636d70f7ae1461035b578063741040021461035257806374de4ec41461034957806374f237c41461034057806379ba5097146103375780637a7664601461032e5780637cb64759146103255780637e1a37861461031c5780638019e7d01461031357806383db28a01461030a57806387e900b1146103015780638856398f146102f85780638932a90d146102ef5780638a44f337146102e65780638da5cb5b146102dd5780638fb4b573146102d45780639a109bc2146102cb5780639d0a3864146102c2578063a07aea1c146102b9578063a4c0ed36146102b0578063a7a2f5aa146102a7578063b187bd261461029e578063bfbd9b1b14610295578063c1852f581461028c578063d365a37714610283578063da9c732f1461027a578063e0974ea514610271578063e5f9297314610268578063e937fdaa1461025f578063ebdb56f3146102565763f2fde38b1461024e57600080fd5b61000e613173565b5061000e6130e1565b5061000e612fe2565b5061000e612e50565b5061000e612e34565b5061000e612a3f565b5061000e61255d565b5061000e612535565b5061000e6122e6565b5061000e610bfe565b5061000e6122ca565b5061000e612244565b5061000e611e8f565b5061000e611d96565b5061000e611c91565b5061000e611b17565b5061000e611ae2565b5061000e611728565b5061000e61151a565b5061000e6114d0565b5061000e6114a8565b5061000e611456565b5061000e611437565b5061000e61140c565b5061000e6113bf565b5061000e611362565b5061000e611235565b5061000e611176565b5061000e610fe4565b5061000e610f9f565b5061000e610f4e565b5061000e610cfa565b5061000e610cab565b5061000e610c25565b5061000e610a3d565b5061000e610a0c565b5061000e610977565b5061000e61089f565b5061000e610880565b5061000e610864565b5061000e610842565b5061000e6106c4565b5061000e61069e565b5061000e610669565b5061000e6105cc565b5061000e61056d565b5061000e6104bd565b5061000e610490565b5061000e610436565b503461000e57600060031936011261000e5760206bffffffffffffffffffffffff60045460101c16604051908152f35b503461000e57600060031936011261000e57600554604080517f0000000000000000000000000000000000000000000000000000000000000000815260609290921c69ffffffffffffffffffff16602083015290f35b0390f35b503461000e57600060031936011261000e5760206bffffffffffffffffffffffff60055416604051908152f35b503461000e57600060031936011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b919082519283825260005b8481106105595750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161051a565b503461000e57600060031936011261000e5761048c60405161058e81611d12565b600d81527f5374616b696e6720302e312e3000000000000000000000000000000000000000602082015260405191829160208352602083019061050f565b503461000e57600060031936011261000e5760206105e8615ab9565b64e8d4a5100061063c6105f9614dad565b926bffffffffffffffffffffffff600a541693810390811161065c575b61063761062161393a565b9169ffffffffffffffffffff60095416906134e6565b6134e6565b04810390811161064f575b604051908152f35b6106576133cf565b610647565b6106646133cf565b610616565b503461000e57600060031936011261000e57602073ffffffffffffffffffffffffffffffffffffffff600f5416604051908152f35b503461000e57600060031936011261000e5760206106ba614b09565b6040519015158152f35b503461000e5760008060031936011261083f576106df614b09565b6108075761077e602061073e6107396106f733615648565b92917f204fccf0d92ed8d48f204adb39b2e81e92bad0dedb93f5716ca9478cfb57de0060806040513381528389820152846040820152866060820152a161356d565b61356d565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915291829081906044820190565b03818573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af180156107fa575b6107cc575b50604051f35b6107ec9060203d81116107f3575b6107e48183611d2e565b8101906139da565b50386107c6565b503d6107da565b6108026139f2565b6107c1565b604490604051907fa30a70c2000000000000000000000000000000000000000000000000000000008252600160048301526024820152fd5b80fd5b503461000e57600060031936011261000e57602060ff60085416604051908152f35b503461000e57600060031936011261000e576020610647615ab9565b503461000e57600060031936011261000e576020601154604051908152f35b503461000e57600060031936011261000e576108b96132a6565b60015460ff8160a01c1615610919577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff166001557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b503461000e57600060031936011261000e576109916132a6565b740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff6001546109db60ff8260a01c161561409e565b16176001557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b503461000e57600060031936011261000e57600c54600b546040805163ffffffff9093168352602083019190915290f35b503461000e5760008060031936011261083f57610a58614b09565b61080757610ab2610aa1610a8c3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460701c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff1690565b8015610bc957602081610ad2610acd61077e9460065461348b565b600655565b610b27610aff3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff8154169055565b7f204fccf0d92ed8d48f204adb39b2e81e92bad0dedb93f5716ca9478cfb57de0060405180610b86843383606090600092949373ffffffffffffffffffffffffffffffffffffffff608083019616825260208201528260408201520152565b0390a16040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915291829081906044820190565b6040517fe4adde72000000000000000000000000000000000000000000000000000000008152336004820152602490fd5b0390fd5b503461000e57600060031936011261000e57602060ff60015460a01c166040519015158152f35b503461000e57600060031936011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b90815180825260208080930193019160005b828110610c81575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610c73565b503461000e57600060031936011261000e5761048c610cc8614103565b604051918291602083526020830190610c61565b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e57602060031936011261000e57600435610d1881610cdc565b610d206132a6565b803b158015610f2f575b8015610f0b575b8015610ee7575b8015610e39575b610e0f57610e0a7f5c74c441be501340b2713817a6c6975e6f3d4a4ae39fa1ac0bf75d3c54a0cad391610d957fffffffffffffffffffffffff0000000000000000000000000000000000000000600f5416600f55565b610dda8173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600d541617600d55565b610de342600e55565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390a1005b60046040517f367a1038000000000000000000000000000000000000000000000000000000008152fd5b506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527fa4c0ed3600000000000000000000000000000000000000000000000000000000600482015260208160248173ffffffffffffffffffffffffffffffffffffffff86165afa908115610eda575b600091610ebc575b5015610d3f565b610ed4915060203d81116107f3576107e48183611d2e565b38610eb5565b610ee26139f2565b610ead565b50600f5473ffffffffffffffffffffffffffffffffffffffff828116911614610d38565b50600d5473ffffffffffffffffffffffffffffffffffffffff828116911614610d31565b503073ffffffffffffffffffffffffffffffffffffffff821614610d2a565b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff600435610f8181610cdc565b166000526002602052602060ff604060002054166040519015158152f35b503461000e57600060031936011261000e576020610fbb614dad565b64e8d4a5100061063c6bffffffffffffffffffffffff600a5460601c169261063761062161393a565b503461000e57602060031936011261000e577fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d610e0a6004356110256132a6565b61102d6159c3565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810182905260208160648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af18015611169575b61114b575b5061113b6110d1610aa16005546bffffffffffffffffffffffff1690565b6110d9615ab9565b906111016110f260095469ffffffffffffffffffff1690565b69ffffffffffffffffffff1690565b9061110a614b20565b91611113614dad565b937f00000000000000000000000000000000000000000000000000000000000000009261357a565b6040519081529081906020820190565b6111629060203d81116107f3576107e48183611d2e565b50386110b3565b6111716139f2565b6110ae565b503461000e57602060031936011261000e576004356111936132a6565b61119b6159c3565b801561000e5760207f1e3be2efa25bca5bff2215c7b30b31086e703d6aa7d9b9a1f8ba62c5291219ad9161122c6111d0614dad565b6111d981613c86565b6111e1613bb5565b6bffffffffffffffffffffffff600554166111fa615ab9565b9084611204614b20565b927f00000000000000000000000000000000000000000000000000000000000000009261357a565b604051908152a1005b503461000e5760008060031936011261083f5773ffffffffffffffffffffffffffffffffffffffff8060015416330361130457815473ffffffffffffffffffffffffffffffffffffffff16600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556112d77fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b604051913391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08484a3f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152fd5b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff60043561139581610cdc565b16600052600260205260206bffffffffffffffffffffffff60406000205460101c16604051908152f35b503461000e57602060031936011261000e577f1b930366dfeaa7eb3b325021e4ae81e36527063452ee55b86c95f85b36f4c31c60206004356113ff6132a6565b80601155604051908152a1005b503461000e57600060031936011261000e57602069ffffffffffffffffffff60095416604051908152f35b503461000e57600060031936011261000e576020600654604051908152f35b503461000e57600060031936011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e57602060031936011261000e5760206106476004356114cb81610cdc565b614d24565b503461000e57600060031936011261000e57600554604080517f0000000000000000000000000000000000000000000000000000000000000000815260b09290921c602083015290f35b503461000e57602060031936011261000e5767ffffffffffffffff60043581811161000e573660238201121561000e57806004013591821161000e576024810190602483369201011161000e5761156f614b09565b6116f05773ffffffffffffffffffffffffffffffffffffffff91826115a9600f5473ffffffffffffffffffffffffffffffffffffffff1690565b1615610e0f576116989260209260006107396116626116236115ca33615648565b9194907f667838b33bdc898470de09e0e746990f2adc11b965b7fe6828e502ebc39e0434604051806116018d8c88878d33876141c5565b0390a1600f5473ffffffffffffffffffffffffffffffffffffffff169561356d565b93611636604051978892338b8501614204565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101875286611d2e565b604051968795869485937f4000aea000000000000000000000000000000000000000000000000000000000855260048501614231565b03927f0000000000000000000000000000000000000000000000000000000000000000165af180156116e3575b6116cc575b005b6116ca9060203d81116107f3576107e48183611d2e565b6116eb6139f2565b6116c5565b60446040517fa30a70c20000000000000000000000000000000000000000000000000000000081526001600482015260006024820152fd5b503461000e57606060031936011261000e576044356004803560243561174c6132a6565b6117546159c3565b818411611aaa576005546bffffffffffffffffffffffff811690838211611a75578269ffffffffffffffffffff8260601c1611611a4057859060b01c11611a075761179d6159d2565b936117df6117bc876106376117b660208a015160ff1690565b60ff1690565b6117d9610aa16040809901516bffffffffffffffffffffffff1690565b9061356d565b84106119d35750938291611839936000960361195e575b806118136110f260055469ffffffffffffffffffff9060601c1690565b036118cd575b508061182a6110f260055460b01c90565b0361183c575b506110d9615ab9565b51f35b6118c4816118b561186d7f816587cb2e773af4f3689a03d7520fabff3462605ded374b485b13994c0d7b5294613980565b75ffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffff000000000000000000000000000000000000000000006005549260b01b16911617600555565b85519081529081906020820190565b0390a138611830565b611955816119466118fe7fb5f554e5ef00806bace1edbb84186512ebcefa2af7706085143f501f29314df794613980565b7fffffffffffffffffffff00000000000000000000ffffffffffffffffffffffff75ffffffffffffffffffff0000000000000000000000006005549260601b16911617600555565b86519081529081906020820190565b0390a138611819565b6119a361196a846139c1565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006005541617600555565b84518381527f7f4f497e086b2eb55f8a9885ba00d33399bbe0ebcb92ea092834386435a1b9c090602090a16117f6565b84517fbc91aa3300000000000000000000000000000000000000000000000000000000815290810186815281906020010390fd5b50506040517fbc91aa33000000000000000000000000000000000000000000000000000000008152918201928352509081906020010390fd5b6040517fbc91aa3300000000000000000000000000000000000000000000000000000000815280860184815281906020010390fd5b6040517f1f3c387600000000000000000000000000000000000000000000000000000000815280860185815281906020010390fd5b50506040517fbc91aa330000000000000000000000000000000000000000000000000000000081529081019182529081906020010390fd5b503461000e57600060031936011261000e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b503461000e57604060031936011261000e57611b316132a6565b60115415611c6757611b627f0000000000000000000000000000000000000000000000000000000000000000615a1b565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048083019190915230602483015235604482015260208160648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af18015611c5a575b611c3c575b6116ca611c08610aa16005546bffffffffffffffffffffffff1690565b611c10614b20565b907f000000000000000000000000000000000000000000000000000000000000000090602435906139ff565b611c539060203d81116107f3576107e48183611d2e565b5038611beb565b611c626139f2565b611be6565b60046040517f9f8a28f2000000000000000000000000000000000000000000000000000000008152fd5b503461000e57602060031936011261000e576020610647600435611cb481610cdc565b614c08565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117611d0557604052565b611d0d611cb9565b604052565b6040810190811067ffffffffffffffff821117611d0557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611d0557604052565b60209067ffffffffffffffff8111611d89575b60051b0190565b611d91611cb9565b611d82565b503461000e57604060031936011261000e57600435611db481610cdc565b6024359067ffffffffffffffff821161000e573660238301121561000e578160040135611de081611d6f565b92611dee6040519485611d2e565b81845260209160248386019160051b8301019136831161000e57602401905b828210611e335761048c611e218787613325565b60405190151581529081906020820190565b81358152908301908301611e0d565b90602060031983011261000e5760043567ffffffffffffffff9283821161000e578060238301121561000e57816004013593841161000e5760248460051b8301011161000e576024019190565b503461000e57611e9e36611e42565b90611ea76132a6565b63ffffffff600c54161515806121ec575b6121b457611ed4611ece6110f260055460b01c90565b836134e6565b611edc615ae7565b80821161217957505060005b828110611f49576116ca611f1584611f0f611f0960045460ff9060081c1690565b91614090565b90615401565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff006004549260081b16911617600455565b611f8f611f88611f62611f5d848787613fdf565b613ff7565b73ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460ff1690565b61211c57611fbf610aa1611faa611f62611f5d858888613fdf565b5460101c6bffffffffffffffffffffffff1690565b6120be57611fda610aa1610a8c611f62611f5d858888613fdf565b6120605780612021611ff6611f62611f5d61205b958888613fdf565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b7fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d612053610de3611f5d848888613fdf565b0390a16133ff565b611ee8565b611f5d90610bfa9361207193613fdf565b6040517f7a378b9c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b611f5d90610bfa936120cf93613fdf565b6040517f602d4d1100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b611f5d90610bfa9361212d93613fdf565b6040517ea5216600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b6040517f35cf446b00000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b60446040517fa30a70c20000000000000000000000000000000000000000000000000000000081526000600482015260016024820152fd5b506121f5614b09565b15611eb8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff8111612237575b01160190565b61223f611cb9565b612231565b503461000e57606060031936011261000e5760043561226281610cdc565b6044359067ffffffffffffffff821161000e573660238301121561000e5781600401359061228f826121fb565b9161229d6040519384611d2e565b808352366024828601011161000e5760208160009260246116ca9701838701378401015260243590614ded565b503461000e57600060031936011261000e576020610647614dad565b503461000e576122f536611e42565b6122fd6132a6565b60005b6003548110156123725780612368612340611f6261232061236d95615b42565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff8154169055565b6133ff565b612300565b5061237b615b87565b60005b8181106123c15750816123b2827f40aed8e423b39a56b445ae160f4c071fc2cfb48ee0b6dcd5ffeb6bc5b18d10d094615bda565b610e0a60405192839283615cb7565b6123cf611f5d828486613fdf565b6124036123ff611f888373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b1590565b6124ec5761243e6124348273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460081c60ff1690565b6124a4579061236861247361249f9373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b6101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff825416179055565b61237e565b6040517ea5216600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b6040517feac13dcd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e57602060031936011261000e5760206106ba60043561255881610cdc565b61496a565b503461000e5761256c36611e42565b906125756132a6565b61257d6159c3565b61258d612588614dad565b613c86565b60005b8281106125b4576116ca611f156125a685614090565b60045460081c60ff1661407c565b6125c2611f5d828585613fdf565b6125f46125ef8273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b614001565b906126026123ff8351151590565b6124ec5760208201516129f6576126df92917f2360404a74478febece1a14f11275f22ada88d19ef96f7d785913010bfff447991612053612656610aa16040809501516bffffffffffffffffffffffff1690565b92836126e4575b6126b261268a8473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055565b51928392836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b612590565b6127f66127ac6126fb6126f686614c08565b6139c1565b61275861271f600a9261271a84546bffffffffffffffffffffffff1690565b613dcf565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a541617600a55565b61278761271f6127726126f661276c61393a565b8b6138d2565b83546bffffffffffffffffffffffff16613dcf565b61271a6127966126f688614d24565b915460601c6bffffffffffffffffffffffff1690565b7fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b61283b61280d61280860085460ff1690565b61404b565b60ff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006008541617600855565b6128906128688473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff8154169055565b61299c610acd6bffffffffffffffffffffffff6128ac876139c1565b61291a6128ce8261271a6004546bffffffffffffffffffffffff9060701c1690565b7fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff79ffffffffffffffffffffffff00000000000000000000000000006004549260701b16911617600455565b612993816129488973ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b907fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff79ffffffffffffffffffffffff000000000000000000000000000083549260701b169116179055565b1660065461356d565b6129f16129c98473ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008154169055565b61265d565b6040517fded6031900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e5760008060031936011261083f57612a5a6159c3565b612a8a610aa1611faa3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b8015612e0a5760409081517ffeaf968c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9160a082600481867f0000000000000000000000000000000000000000000000000000000000000000165afa918215612dfd575b85908693612dbd575b5069ffffffffffffffffffff16916010548314612d8c57612b4b7f00000000000000000000000000000000000000000000000000000000000000008261356d565b4210612d2d57612b7c907f00000000000000000000000000000000000000000000000000000000000000009061356d565b4210908180612d56575b612d2d5792827fd2720e8f454493f612cc97499fe8cbce7fa4d4c18d346fe7104e9042df1c1edd612bc6612c21948997612bc1602098601055565b61596b565b875133815260208101939093526040830181905291606090a185517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915293849283919082906044820190565b03927f0000000000000000000000000000000000000000000000000000000000000000165af18015612d20575b612d02575b50612cae612c5f614dad565b612c67614103565b7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000614366565b611839612ccb610aa16005546bffffffffffffffffffffffff1690565b612cd3615ab9565b612ceb6110f260095469ffffffffffffffffffff1690565b612cf3614b20565b91612cfc614dad565b936136c4565b612d199060203d81116107f3576107e48183611d2e565b5038612c53565b612d286139f2565b612c4e565b600485517ffc53c50a000000000000000000000000000000000000000000000000000000008152fd5b50612d876123ff611f883373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b612b86565b84517ff3553c2200000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b69ffffffffffffffffffff9350612deb915060a03d8111612df6575b612de38183611d2e565b81019061427a565b50949150612b0a9050565b503d612dd9565b612e056139f2565b612b01565b60046040517fef67f5d8000000000000000000000000000000000000000000000000000000008152fd5b503461000e57600060031936011261000e576020610647614b20565b503461000e57600060031936011261000e57612e6a6132a6565b612e726159c3565b612e7a615ab9565b612f437fffffffffffffffff00000000000000000000000000000000000000000000000077ffffffffffffffffffffffff000000000000000000000000612f73612f27612f21612f1b612ecb614dad565b97612ed589613c86565b612edd613bb5565b888103908111612fd5575b610637612ef361393a565b9169ffffffffffffffffffff60095416948591612f1b8561063764e8d4a51000998a946134e6565b046139c1565b9a6134e6565b600a54956bffffffffffffffffffffffff958691828916613dcf565b1694857fffffffffffffffffffffffffffffffffffffffff00000000000000000000000088161760601c16613dcf565b60601b1692161717600a5542600b557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006004541660045560006040517ff7d0e0f15586495da8c687328ead30fb829d9da55538cb0ef73dd229e517cdb88282a1f35b612fdd6133cf565b612ee8565b503461000e57600060031936011261000e57612ffc6132a6565b600d5473ffffffffffffffffffffffffffffffffffffffff168015610e0f57613026600e5461355d565b4210612e0a576130719073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600f541617600f55565b61309e7fffffffffffffffffffffffff0000000000000000000000000000000000000000600d5416600d55565b7ffa33c052bbee754f3c0482a89962daffe749191fa33c696a61e947fbfd68bd84610e0a610de3600f5473ffffffffffffffffffffffffffffffffffffffff1690565b503461000e5760008060031936011261083f576130fc6132a6565b613104614b09565b6108075761077e6020613141613118614b20565b600a54906131366bffffffffffffffffffffffff918284169061348b565b9160601c169061348b565b6040518181527f150a6ec0e6f4e9ddcaaaa1674f157d91165a42d60653016f87a9fc870a39f050908060208101610b86565b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff6004356131a681610cdc565b6131ae6132a6565b1633811461324857806000917fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015561321d613204835473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b90604051917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788484a3f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff6000541633036132c757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152fd5b90601154801561336c576133699260405173ffffffffffffffffffffffffffffffffffffffff60208201921682526020815261336081611d12565b51902091613374565b90565b505050600190565b929091906000915b84518310156133c75761338f8386613469565b51908181116133b2576000526020526133ac6040600020926133ff565b9161337c565b906000526020526133ac6040600020926133ff565b915092501490565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461342d570190565b6134356133cf565b0190565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602091815181101561347e575b60051b010190565b613486613439565b613476565b9190820391821161349857565b6134a06133cf565b565b64e8d4a5100090807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166134da570290565b6134e26133cf565b0290565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166134da570290565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115613551570490565b613559613517565b0490565b9062093a80820180921161349857565b9190820180921161349857565b929394916135df906135d961358f85856138f5565b956135d36135b061359f88613957565b8b8a829d039081116136b75761348b565b6135c86110f260095469ffffffffffffffffffff1690565b850361366f576134a2565b926134e6565b90613547565b938410613645576136409464e8d4a51000613633866106378661361e61271f6126f661363a9b6117d9876136196127ac9e6126f69e61348b565b6138d2565b69ffffffffffffffffffff60095416906134e6565b049061356d565b4261356d565b600b55565b60046040517fda056d00000000000000000000000000000000000000000000000000000000008152fd5b6136b261367b86613980565b69ffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000006009541617600955565b6134a2565b6136bf6133cf565b61348b565b928491926136d283856138f5565b906137156136df85613957565b96838503948511613826575b878503948511613819575b6135d369ffffffffffffffffffff958660095416850361366f576134a2565b90811561380c575b0461372a8197829661348b565b90613734916138d2565b61373d9161356d565b613746906139c1565b613783906bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a541617600a55565b60095416613790916134e6565b9061379a916134e6565b64e8d4a5100090046137ab9161356d565b6137b4906139c1565b613802907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b613640904261356d565b613814613517565b61371d565b6138216133cf565b6136f6565b61382e6133cf565b6136eb565b6136bf93916135d9613899926135d3600a5469ffffffffffffffffffff61388c6bffffffffffffffffffffffff988984169b8c9a61386f61393a565b506009549560601c169a8b92600b544281116138b6575b5061348b565b9116850361366f576134a2565b918210613645576127ac61363a916126f661271f613640966139c1565b428103116138c5575b38613886565b6138cd6133cf565b6138bf565b64e8d4a51000916106376135599269ffffffffffffffffffff60095416906134e6565b61392564e8d4a51000916bffffffffffffffffffffffff600a541693810390811161065c5761063761062161393a565b0481039081116139325790565b6133696133cf565b600b5442811161394a5750600090565b4281039081116139325790565b64e8d4a510006139256bffffffffffffffffffffffff600a5460601c169261063761062161393a565b69ffffffffffffffffffff90818111613997571690565b60046040517f811752de000000000000000000000000000000000000000000000000000000008152fd5b6bffffffffffffffffffffffff90818111613997571690565b9081602091031261000e5751801515810361000e5790565b506040513d6000823e3d90fd5b600c5463ffffffff949385821661000e5783613b3f93613b3a7f125fc8494f786b470e3c39d0932a62e9e09e291ebd81ea19c57604f6d2b1d16798889569ffffffffffffffffffff613a5086613980565b167fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000060095416176009557fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000613aa442613b84565b928316911617600c556008547fffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffff70ffffffff000000000000000000000000008360681b169116176008557fffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffff79ffffffff000000000000000000000000000000000000000000006009549260b01b16911617600955565b613833565b613b7f613b51600c5463ffffffff1690565b600b5490604051948594859094939263ffffffff906060936080840197845260208401521660408201520152565b0390a1565b63ffffffff90818111613997571690565b9190916bffffffffffffffffffffffff8080941691160191821161349857565b613bc2600b544290613fcd565b600954907fffffffffffff00000000000000000000000000000000ffffffffffffffffffff79ffffffff00000000000000000000000000000000000000000000613c6c75ffffffffffffffffffffffff00000000000000000000613c62613c4b6126f663ffffffff8960b01c168803888111613c79575b69ffffffffffffffffffff8a166134e6565b6bffffffffffffffffffffffff8860501c16613b95565b60501b1693613b84565b60b01b1692161717600955565b613c816133cf565b613c39565b6126f6613c9291613cfd565b6008547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff6cffffffffffffffffffffffff0070ffffffff00000000000000000000000000613ceb613ce6600b544290613fcd565b613b84565b60681b169360081b1691161717600855565b61336990613d0d600b5442101590565b15613d82576bffffffffffffffffffffffff613d7864e8d4a51000613d6b613d4f600b54613d4963ffffffff60085460681c1663ffffffff1690565b9061348b565b6106376008549669ffffffffffffffffffff60095416906134e6565b046135d960ff8516613dbe565b9160081c1661356d565b6bffffffffffffffffffffffff613d7864e8d4a51000613d6b63ffffffff60085460681c16420342811115613d4f57613db96133cf565b613d4f565b60018110613dc95790565b50600190565b6bffffffffffffffffffffffff918216908216039190821161349857565b6001906bffffffffffffffffffffffff80911690811461342d570190565b6127ac906134a092613e1b61393a565b69ffffffffffffffffffff6009541691610637613e5984612f1b85610637613e528a612f1b64e8d4a510009a8b998a9889946134e6565b98876134e6565b9406613ecd575b06613eb8575b613ea7907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a54916bffffffffffffffffffffffff938491828516613b95565b1691161780600a5560601c16613b95565b90613ec5613ea791613ded565b919050613e66565b91613ed790613ded565b91613e60565b613f556134a09164e8d4a51000613f1181612f1b613ef961393a565b61063769ffffffffffffffffffff60095416876134e6565b9106613fbf575b600a547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006bffffffffffffffffffffffff80948194828516613b95565b1691161780600a5560601c16908111613fb2577fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b613fba6133cf565b6127ac565b613fc890613ded565b613f18565b9080821015613fda575090565b905090565b9190811015613fef5760051b0190565b611d91613439565b3561336981610cdc565b9060405161400e81611ce9565b606081935460ff81161515835260ff8160081c16151560208401526bffffffffffffffffffffffff90818160101c16604085015260701c16910152565b60ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9116019060ff821161349857565b9060ff8091169116039060ff821161349857565b60ff81116139975760ff1690565b156140a557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b60405190600354808352826020918282019060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b936000905b828210614156575050506134a092500383611d2e565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958895509381019390910190614140565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b919260a09373ffffffffffffffffffffffffffffffffffffffff6133699896931684526020840152604083015260608201528160808201520191614186565b60409073ffffffffffffffffffffffffffffffffffffffff61336995931681528160208201520191614186565b613369939273ffffffffffffffffffffffffffffffffffffffff6060931682526020820152816040820152019061050f565b519069ffffffffffffffffffff8216820361000e57565b908160a091031261000e5761428e81614263565b91602082015191604081015191613369608060608401519301614263565b906142b682611d6f565b6142c36040519182611d2e565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06142f18294611d6f565b0190602036910137565b90815180825260208080930193019160005b82811061431b575050505090565b83518552938101939281019260010161430d565b916143589061434a6133699593606086526060860190610c61565b9084820360208601526142fb565b9160408184039101526142fb565b939291909360ff61437960085460ff1690565b1615614522578264e8d4a510006143a6876106376143ad9569ffffffffffffffffffff60095416906134e6565b0495614529565b91600091826143bc82516142ac565b946143c783516142ac565b92855b81518710156144a1576143fa6143e08884613469565b5173ffffffffffffffffffffffffffffffffffffffff1690565b8861442b610aa1611faa8473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b8c811561449257898461447a946144578e61445161448b9a976144859a61445e986145b5565b92613469565b5288614723565b6144688b8a613469565b526144738a8c613469565b519061356d565b946144738988613469565b966133ff565b95926143ca565b50505050929561448b906133ff565b7e635ea9da6e262e92bb713d71840af7c567807ff35bf73e927490c6128324809899506144fd919650613b7f95506127ac9250926126f661271f6144e7614516966139c1565b600a546bffffffffffffffffffffffff16613dcf565b600a5460601c6bffffffffffffffffffffffff16613dcf565b6040519384938461432f565b5050509050565b64e8d4a5100061459960ff92610637604051956060870187811067ffffffffffffffff8211176145a8575b60405263ffffffff60085487811689526bffffffffffffffffffffffff8160081c1660208a015260681c16604088015269ffffffffffffffffffff60095416906134e6565b04915116908115613551570490565b6145b0611cb9565b614554565b916146b061467361336993946145cd600b5442101590565b156146f45761463761462c6145f0600b5463ffffffff60095460b01c169061348b565b955b61462661460e6009549869ffffffffffffffffffff8a166134e6565b6bffffffffffffffffffffffff809960501c1661356d565b906134e6565b64e8d4a51000900490565b90846146638873ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b541682039182116146e757613fcd565b936146a7614680866139c1565b9173ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b92835416613b95565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000825416179055565b6146ef6133cf565b613fcd565b61463761462c63ffffffff60095460b01c164203428111614716575b956145f2565b61471e6133cf565b614710565b916148066147ed613369939461473b600b5442101590565b1561484e576147ae61479664e8d4a5100061478961476d600b54613d4963ffffffff60085460681c1663ffffffff1690565b6106376008549969ffffffffffffffffffff60095416906134e6565b046135d960ff8816613dbe565b6bffffffffffffffffffffffff809660081c1661356d565b90846147da8873ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5460601c1682039182116146e757613fcd565b936147fa614680866139c1565b92835460601c16613b95565b7fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b6147ae61479664e8d4a5100061478963ffffffff60085460681c1642034281111561476d5761487b6133cf565b61476d565b600b546133699161462c914281116148f45763ffffffff60095460b01c1681039081116148e7575b905b6148d56009549269ffffffffffffffffffff6bffffffffffffffffffffffff8560501c1694166134e6565b8201809211156134e6576106376133cf565b6148ef6133cf565b6148a8565b5063ffffffff60095460b01c164203428111614911575b906148aa565b6149196133cf565b61490b565b9061493d73ffffffffffffffffffffffffffffffffffffffff91613cfd565b911660005260076020526bffffffffffffffffffffffff60406000205460601c1681039081116139325790565b61499a610aa1611faa8373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b15614b03576149aa6123ff614b09565b614b03576040517ffeaf968c00000000000000000000000000000000000000000000000000000000815260a08160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115614af6575b6000908192614ad1575b5069ffffffffffffffffffff601054911614614aca57614a657f00000000000000000000000000000000000000000000000000000000000000008261356d565b4210614aca57614a96907f00000000000000000000000000000000000000000000000000000000000000009061356d565b421015613dc957611f886133699173ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5050600090565b9050614aeb915060a03d8111612df657612de38183611d2e565b509291505038614a25565b614afe6139f2565b614a1b565b50600090565b60ff6004541680614b175790565b50600b54421090565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115614bfb575b600091614bca575b50614ba4615ab9565b8103908111614bbd575b60065481039081116139325790565b614bc56133cf565b614bae565b906020823d8211614bf3575b81614be360209383611d2e565b8101031261083f57505138614b9b565b3d9150614bd6565b614c036139f2565b614b93565b73ffffffffffffffffffffffffffffffffffffffff81169060009180835260026020526bffffffffffffffffffffffff80604085205460101c16938415614cf95760ff604082205416614cd357505050613d49610aa1614cc1614c9a614c9561336996613d497f000000000000000000000000000000000000000000000000000000000000000082613547565b614880565b9373ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b546bffffffffffffffffffffffff1690565b909250614ce260409294614880565b938152600760205220541681039081116139325790565b935050505090565b908015614d17575b810481039081116139325790565b614d1f613517565b614d09565b73ffffffffffffffffffffffffffffffffffffffff81166000526002602052604060002060405190614d5582611ce9565b549060ff821615908115815260ff8360081c161515602082015260606bffffffffffffffffffffffff808560101c169485604085015260701c16910152614aca5715614b035761336990614da7614dad565b9061491e565b6bffffffffffffffffffffffff60045460101c167f0000000000000000000000000000000000000000000000000000000000000000908115613551570490565b9173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169182330361502657614e3f60ff60015460a01c161561409e565b614e476159c3565b809264e8d4a51000808310614ff05782069182614f3f575b505050614e8f611f888473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b15614e9e57506134a091615415565b6011549081614eb3575b50506134a0916150ca565b805115612e0a57614f3491614ed4826020806123ff95518301019101615050565b90604051602081019061336081614f088a8591909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611d2e565b612e0a573880614ea8565b829450602091614f54600094614fae9361348b565b956040519485809481937fa9059cbb0000000000000000000000000000000000000000000000000000000083528b600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015614fe3575b614fc5575b8080614e5f565b614fdc9060203d81116107f3576107e48183611d2e565b5038614fbe565b614feb6139f2565b614fb9565b6040517f1d820b1700000000000000000000000000000000000000000000000000000000815264e8d4a510006004820152602490fd5b60046040517f4d695438000000000000000000000000000000000000000000000000000000008152fd5b602090818184031261000e5780519067ffffffffffffffff821161000e57019180601f8401121561000e57825161508681611d6f565b936150946040519586611d2e565b818552838086019260051b82010192831161000e578301905b8282106150bb575050505090565b815181529083019083016150ad565b91906150fc610aa1611faa8573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b92615107828561356d565b937f00000000000000000000000000000000000000000000000000000000000000008086106153bc575060055461514c9060601c69ffffffffffffffffffff166110f2565b80861161537857505061515d615ae7565b8083116153455750613b7f7f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909394615196612588614dad565b6152357f000000000000000000000000000000000000000000000000000000000000000061522f6151d06151ca8389613547565b8861348b565b916152296151e06126f685614880565b6146b061520d8a73ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b9161522483546bffffffffffffffffffffffff1690565b613b95565b87613547565b90613e0b565b61529d61525d615244866139c1565b60045460101c6bffffffffffffffffffffffff16613b95565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff6dffffffffffffffffffffffff00006004549260101b16911617600455565b6153126152a9826139c1565b6152d38573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b907fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff6dffffffffffffffffffffffff000083549260101b169116179055565b6040519384938460409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b6040517fb94339d80000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b610bfa916153859161348b565b6040519182917fb94339d8000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b6040517f1d820b170000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b60ff60019116019060ff821161349857565b9060ff8091169116019060ff821161349857565b9190615447610aa1611faa8573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b92615452828561356d565b937f00000000000000000000000000000000000000000000000000000000000000008086106153bc575061548b6110f260055460b01c90565b80861161537857507f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909394613b7f911561552d575b6154fc6154cf6126f686614880565b6146b061520d8673ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b6155246128ce61550b866139c1565b60045460701c6bffffffffffffffffffffffff16613b95565b61529d84613edd565b615538612588614dad565b61555361280d60085460ff81169081156155e2575b506153ef565b600880546155dd911c6bffffffffffffffffffffffff166155948573ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b6154c0565b6127ac6bffffffffffffffffffffffff6156159260081c1661271a600a546bffffffffffffffffffffffff9060601c1690565b6156427fffffffffffffffffffffffffffffffffffffff000000000000000000000000ff60085416600855565b3861554d565b6156756125ef8273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b9160408301926bffffffffffffffffffffffff90816156a086516bffffffffffffffffffffffff1690565b16156158cc576156c1906156b660045460ff1690565b615887575b51151590565b156157ed57506156fc6128ce6156e385516bffffffffffffffffffffffff1690565b60045460701c6bffffffffffffffffffffffff16613dcf565b6157e8610aa16157248461571f610aa188516bffffffffffffffffffffffff1690565b615913565b946157b861286861579161574c610aa16008546bffffffffffffffffffffffff9060081c1690565b613d49610aa161577c8b73ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5460601c6bffffffffffffffffffffffff1690565b9673ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b6157c761271f6144e7886139c1565b6157d66127ac6144fd876139c1565b516bffffffffffffffffffffffff1690565b929190565b836158716128686157918661571f610aa19661583961525d6158206158809b9c9d516bffffffffffffffffffffffff1690565b60045460101c6bffffffffffffffffffffffff16613dcf565b86516bffffffffffffffffffffffff167f00000000000000000000000000000000000000000000000000000000000000009116614d01565b6157d661271f6144e7876139c1565b9190600090565b615892612588614dad565b61589a613bb5565b6158c77fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060045416600455565b6156bb565b6040517fe4adde7200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602490fd5b73ffffffffffffffffffffffffffffffffffffffff64e8d4a5100061594d6bffffffffffffffffffffffff938460095460501c16906134e6565b04921660005260076020526040600020541681039081116139325790565b9061599e576133699060057f00000000000000000000000000000000000000000000000000000000000000009104613fcd565b507f000000000000000000000000000000000000000000000000000000000000000090565b6159cb614b09565b156121b457565b604051906159df82611ce9565b81606060045460ff81161515835260ff8160081c1660208401526bffffffffffffffffffffffff90818160101c16604085015260701c16910152565b6004549060ff8260081c1690808210615a825750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060019116176004557fded6ebf04e261e1eb2f3e3b268a2e6aee5b478c15b341eba5cf18b9bc80c2e636000604051a1565b60449250604051917fe709379900000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b615ac16159d2565b6bffffffffffffffffffffffff6060816040840151169201511681018091116139325790565b615aef6159d2565b6005549060406bffffffffffffffffffffffff91615b1c8385169460ff6020840151169060b01c906134e6565b8403938411615b35575b01511681039081116139325790565b615b3d6133cf565b615b26565b600354811015615b7a575b60036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b615b82613439565b615b4d565b60035460008060035581615b99575050565b600381527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b918201915b828110615bcf57505050565b818155600101615bc3565b67ffffffffffffffff8211615caa575b680100000000000000008211615c9d575b60035482600355808310615c5c575b50600360005260005b828110615c1f57505050565b60019060208335615c2f81610cdc565b930192817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b015501615c13565b827fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b91820191015b818110615c915750615c0a565b60008155600101615c84565b615ca5611cb9565b615bfb565b615cb2611cb9565b615bea565b90916040602092828482018583525201929160005b828110615cda575050505090565b90919293828060019273ffffffffffffffffffffffffffffffffffffffff8835615d0381610cdc565b16815201950193929101615ccc56fea164736f6c6343000810000a000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841900000000000000000000000000000000000000000014adf4b7320334b900000000000000000000000000000000000000000000000000017b7883c06916600000000000000000000000000000000000000000000000000a968163f0a57b4000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002ee000000000000000000000000000000000000000000000017b7883c0691660000000000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000278d00000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000000014
Deployed Bytecode
0x60806040526004361015610013575b600080fd5b60003560e01c8063049b2ca0146104065780630641bdd8146103fd5780630fbc8f5b146103f4578063165d35e1146103eb578063181f5a77146103e25780631a9d4c7c146103d95780631ddb5552146103d057806322f3e2d4146103c75780632def6620146103be57806332e28850146103b557806338adb6f0146103ac57806349590657146103a35780634a4e3bd51461039a57806351858e271461039157806359f01879146103885780635aa6e0131461037f5780635c975abb1461029e5780635e8b40d7146103765780635fec60f81461036d57806363b2c85a146103645780636d70f7ae1461035b578063741040021461035257806374de4ec41461034957806374f237c41461034057806379ba5097146103375780637a7664601461032e5780637cb64759146103255780637e1a37861461031c5780638019e7d01461031357806383db28a01461030a57806387e900b1146103015780638856398f146102f85780638932a90d146102ef5780638a44f337146102e65780638da5cb5b146102dd5780638fb4b573146102d45780639a109bc2146102cb5780639d0a3864146102c2578063a07aea1c146102b9578063a4c0ed36146102b0578063a7a2f5aa146102a7578063b187bd261461029e578063bfbd9b1b14610295578063c1852f581461028c578063d365a37714610283578063da9c732f1461027a578063e0974ea514610271578063e5f9297314610268578063e937fdaa1461025f578063ebdb56f3146102565763f2fde38b1461024e57600080fd5b61000e613173565b5061000e6130e1565b5061000e612fe2565b5061000e612e50565b5061000e612e34565b5061000e612a3f565b5061000e61255d565b5061000e612535565b5061000e6122e6565b5061000e610bfe565b5061000e6122ca565b5061000e612244565b5061000e611e8f565b5061000e611d96565b5061000e611c91565b5061000e611b17565b5061000e611ae2565b5061000e611728565b5061000e61151a565b5061000e6114d0565b5061000e6114a8565b5061000e611456565b5061000e611437565b5061000e61140c565b5061000e6113bf565b5061000e611362565b5061000e611235565b5061000e611176565b5061000e610fe4565b5061000e610f9f565b5061000e610f4e565b5061000e610cfa565b5061000e610cab565b5061000e610c25565b5061000e610a3d565b5061000e610a0c565b5061000e610977565b5061000e61089f565b5061000e610880565b5061000e610864565b5061000e610842565b5061000e6106c4565b5061000e61069e565b5061000e610669565b5061000e6105cc565b5061000e61056d565b5061000e6104bd565b5061000e610490565b5061000e610436565b503461000e57600060031936011261000e5760206bffffffffffffffffffffffff60045460101c16604051908152f35b503461000e57600060031936011261000e57600554604080517f0000000000000000000000000000000000000000000000000de0b6b3a7640000815260609290921c69ffffffffffffffffffff16602083015290f35b0390f35b503461000e57600060031936011261000e5760206bffffffffffffffffffffffff60055416604051908152f35b503461000e57600060031936011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca168152f35b919082519283825260005b8481106105595750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161051a565b503461000e57600060031936011261000e5761048c60405161058e81611d12565b600d81527f5374616b696e6720302e312e3000000000000000000000000000000000000000602082015260405191829160208352602083019061050f565b503461000e57600060031936011261000e5760206105e8615ab9565b64e8d4a5100061063c6105f9614dad565b926bffffffffffffffffffffffff600a541693810390811161065c575b61063761062161393a565b9169ffffffffffffffffffff60095416906134e6565b6134e6565b04810390811161064f575b604051908152f35b6106576133cf565b610647565b6106646133cf565b610616565b503461000e57600060031936011261000e57602073ffffffffffffffffffffffffffffffffffffffff600f5416604051908152f35b503461000e57600060031936011261000e5760206106ba614b09565b6040519015158152f35b503461000e5760008060031936011261083f576106df614b09565b6108075761077e602061073e6107396106f733615648565b92917f204fccf0d92ed8d48f204adb39b2e81e92bad0dedb93f5716ca9478cfb57de0060806040513381528389820152846040820152866060820152a161356d565b61356d565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915291829081906044820190565b03818573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca165af180156107fa575b6107cc575b50604051f35b6107ec9060203d81116107f3575b6107e48183611d2e565b8101906139da565b50386107c6565b503d6107da565b6108026139f2565b6107c1565b604490604051907fa30a70c2000000000000000000000000000000000000000000000000000000008252600160048301526024820152fd5b80fd5b503461000e57600060031936011261000e57602060ff60085416604051908152f35b503461000e57600060031936011261000e576020610647615ab9565b503461000e57600060031936011261000e576020601154604051908152f35b503461000e57600060031936011261000e576108b96132a6565b60015460ff8160a01c1615610919577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff166001557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152fd5b503461000e57600060031936011261000e576109916132a6565b740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff6001546109db60ff8260a01c161561409e565b16176001557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b503461000e57600060031936011261000e57600c54600b546040805163ffffffff9093168352602083019190915290f35b503461000e5760008060031936011261083f57610a58614b09565b61080757610ab2610aa1610a8c3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460701c6bffffffffffffffffffffffff1690565b6bffffffffffffffffffffffff1690565b8015610bc957602081610ad2610acd61077e9460065461348b565b600655565b610b27610aff3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff8154169055565b7f204fccf0d92ed8d48f204adb39b2e81e92bad0dedb93f5716ca9478cfb57de0060405180610b86843383606090600092949373ffffffffffffffffffffffffffffffffffffffff608083019616825260208201528260408201520152565b0390a16040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915291829081906044820190565b6040517fe4adde72000000000000000000000000000000000000000000000000000000008152336004820152602490fd5b0390fd5b503461000e57600060031936011261000e57602060ff60015460a01c166040519015158152f35b503461000e57600060031936011261000e5760206040517f00000000000000000000000000000000000000000000000000000000000000148152f35b90815180825260208080930193019160005b828110610c81575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610c73565b503461000e57600060031936011261000e5761048c610cc8614103565b604051918291602083526020830190610c61565b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e57602060031936011261000e57600435610d1881610cdc565b610d206132a6565b803b158015610f2f575b8015610f0b575b8015610ee7575b8015610e39575b610e0f57610e0a7f5c74c441be501340b2713817a6c6975e6f3d4a4ae39fa1ac0bf75d3c54a0cad391610d957fffffffffffffffffffffffff0000000000000000000000000000000000000000600f5416600f55565b610dda8173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600d541617600d55565b610de342600e55565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390a1005b60046040517f367a1038000000000000000000000000000000000000000000000000000000008152fd5b506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527fa4c0ed3600000000000000000000000000000000000000000000000000000000600482015260208160248173ffffffffffffffffffffffffffffffffffffffff86165afa908115610eda575b600091610ebc575b5015610d3f565b610ed4915060203d81116107f3576107e48183611d2e565b38610eb5565b610ee26139f2565b610ead565b50600f5473ffffffffffffffffffffffffffffffffffffffff828116911614610d38565b50600d5473ffffffffffffffffffffffffffffffffffffffff828116911614610d31565b503073ffffffffffffffffffffffffffffffffffffffff821614610d2a565b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff600435610f8181610cdc565b166000526002602052602060ff604060002054166040519015158152f35b503461000e57600060031936011261000e576020610fbb614dad565b64e8d4a5100061063c6bffffffffffffffffffffffff600a5460601c169261063761062161393a565b503461000e57602060031936011261000e577fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d610e0a6004356110256132a6565b61102d6159c3565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810182905260208160648160007f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca73ffffffffffffffffffffffffffffffffffffffff165af18015611169575b61114b575b5061113b6110d1610aa16005546bffffffffffffffffffffffff1690565b6110d9615ab9565b906111016110f260095469ffffffffffffffffffff1690565b69ffffffffffffffffffff1690565b9061110a614b20565b91611113614dad565b937f0000000000000000000000000000000000000000000000000000000000278d009261357a565b6040519081529081906020820190565b6111629060203d81116107f3576107e48183611d2e565b50386110b3565b6111716139f2565b6110ae565b503461000e57602060031936011261000e576004356111936132a6565b61119b6159c3565b801561000e5760207f1e3be2efa25bca5bff2215c7b30b31086e703d6aa7d9b9a1f8ba62c5291219ad9161122c6111d0614dad565b6111d981613c86565b6111e1613bb5565b6bffffffffffffffffffffffff600554166111fa615ab9565b9084611204614b20565b927f0000000000000000000000000000000000000000000000000000000000278d009261357a565b604051908152a1005b503461000e5760008060031936011261083f5773ffffffffffffffffffffffffffffffffffffffff8060015416330361130457815473ffffffffffffffffffffffffffffffffffffffff16600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556112d77fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b604051913391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08484a3f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152fd5b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff60043561139581610cdc565b16600052600260205260206bffffffffffffffffffffffff60406000205460101c16604051908152f35b503461000e57602060031936011261000e577f1b930366dfeaa7eb3b325021e4ae81e36527063452ee55b86c95f85b36f4c31c60206004356113ff6132a6565b80601155604051908152a1005b503461000e57600060031936011261000e57602069ffffffffffffffffffff60095416604051908152f35b503461000e57600060031936011261000e576020600654604051908152f35b503461000e57600060031936011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419168152f35b503461000e57602060031936011261000e5760206106476004356114cb81610cdc565b614d24565b503461000e57600060031936011261000e57600554604080517f00000000000000000000000000000000000000000000003635c9adc5dea00000815260b09290921c602083015290f35b503461000e57602060031936011261000e5767ffffffffffffffff60043581811161000e573660238201121561000e57806004013591821161000e576024810190602483369201011161000e5761156f614b09565b6116f05773ffffffffffffffffffffffffffffffffffffffff91826115a9600f5473ffffffffffffffffffffffffffffffffffffffff1690565b1615610e0f576116989260209260006107396116626116236115ca33615648565b9194907f667838b33bdc898470de09e0e746990f2adc11b965b7fe6828e502ebc39e0434604051806116018d8c88878d33876141c5565b0390a1600f5473ffffffffffffffffffffffffffffffffffffffff169561356d565b93611636604051978892338b8501614204565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101875286611d2e565b604051968795869485937f4000aea000000000000000000000000000000000000000000000000000000000855260048501614231565b03927f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca165af180156116e3575b6116cc575b005b6116ca9060203d81116107f3576107e48183611d2e565b6116eb6139f2565b6116c5565b60446040517fa30a70c20000000000000000000000000000000000000000000000000000000081526001600482015260006024820152fd5b503461000e57606060031936011261000e576044356004803560243561174c6132a6565b6117546159c3565b818411611aaa576005546bffffffffffffffffffffffff811690838211611a75578269ffffffffffffffffffff8260601c1611611a4057859060b01c11611a075761179d6159d2565b936117df6117bc876106376117b660208a015160ff1690565b60ff1690565b6117d9610aa16040809901516bffffffffffffffffffffffff1690565b9061356d565b84106119d35750938291611839936000960361195e575b806118136110f260055469ffffffffffffffffffff9060601c1690565b036118cd575b508061182a6110f260055460b01c90565b0361183c575b506110d9615ab9565b51f35b6118c4816118b561186d7f816587cb2e773af4f3689a03d7520fabff3462605ded374b485b13994c0d7b5294613980565b75ffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffff000000000000000000000000000000000000000000006005549260b01b16911617600555565b85519081529081906020820190565b0390a138611830565b611955816119466118fe7fb5f554e5ef00806bace1edbb84186512ebcefa2af7706085143f501f29314df794613980565b7fffffffffffffffffffff00000000000000000000ffffffffffffffffffffffff75ffffffffffffffffffff0000000000000000000000006005549260601b16911617600555565b86519081529081906020820190565b0390a138611819565b6119a361196a846139c1565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006005541617600555565b84518381527f7f4f497e086b2eb55f8a9885ba00d33399bbe0ebcb92ea092834386435a1b9c090602090a16117f6565b84517fbc91aa3300000000000000000000000000000000000000000000000000000000815290810186815281906020010390fd5b50506040517fbc91aa33000000000000000000000000000000000000000000000000000000008152918201928352509081906020010390fd5b6040517fbc91aa3300000000000000000000000000000000000000000000000000000000815280860184815281906020010390fd5b6040517f1f3c387600000000000000000000000000000000000000000000000000000000815280860185815281906020010390fd5b50506040517fbc91aa330000000000000000000000000000000000000000000000000000000081529081019182529081906020010390fd5b503461000e57600060031936011261000e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b503461000e57604060031936011261000e57611b316132a6565b60115415611c6757611b627f0000000000000000000000000000000000000000000000000000000000000032615a1b565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048083019190915230602483015235604482015260208160648160007f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca73ffffffffffffffffffffffffffffffffffffffff165af18015611c5a575b611c3c575b6116ca611c08610aa16005546bffffffffffffffffffffffff1690565b611c10614b20565b907f0000000000000000000000000000000000000000000000000000000000278d0090602435906139ff565b611c539060203d81116107f3576107e48183611d2e565b5038611beb565b611c626139f2565b611be6565b60046040517f9f8a28f2000000000000000000000000000000000000000000000000000000008152fd5b503461000e57602060031936011261000e576020610647600435611cb481610cdc565b614c08565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117611d0557604052565b611d0d611cb9565b604052565b6040810190811067ffffffffffffffff821117611d0557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611d0557604052565b60209067ffffffffffffffff8111611d89575b60051b0190565b611d91611cb9565b611d82565b503461000e57604060031936011261000e57600435611db481610cdc565b6024359067ffffffffffffffff821161000e573660238301121561000e578160040135611de081611d6f565b92611dee6040519485611d2e565b81845260209160248386019160051b8301019136831161000e57602401905b828210611e335761048c611e218787613325565b60405190151581529081906020820190565b81358152908301908301611e0d565b90602060031983011261000e5760043567ffffffffffffffff9283821161000e578060238301121561000e57816004013593841161000e5760248460051b8301011161000e576024019190565b503461000e57611e9e36611e42565b90611ea76132a6565b63ffffffff600c54161515806121ec575b6121b457611ed4611ece6110f260055460b01c90565b836134e6565b611edc615ae7565b80821161217957505060005b828110611f49576116ca611f1584611f0f611f0960045460ff9060081c1690565b91614090565b90615401565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff61ff006004549260081b16911617600455565b611f8f611f88611f62611f5d848787613fdf565b613ff7565b73ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460ff1690565b61211c57611fbf610aa1611faa611f62611f5d858888613fdf565b5460101c6bffffffffffffffffffffffff1690565b6120be57611fda610aa1610a8c611f62611f5d858888613fdf565b6120605780612021611ff6611f62611f5d61205b958888613fdf565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b7fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d612053610de3611f5d848888613fdf565b0390a16133ff565b611ee8565b611f5d90610bfa9361207193613fdf565b6040517f7a378b9c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b611f5d90610bfa936120cf93613fdf565b6040517f602d4d1100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b611f5d90610bfa9361212d93613fdf565b6040517ea5216600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201529081906024820190565b6040517f35cf446b00000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b60446040517fa30a70c20000000000000000000000000000000000000000000000000000000081526000600482015260016024820152fd5b506121f5614b09565b15611eb8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff8111612237575b01160190565b61223f611cb9565b612231565b503461000e57606060031936011261000e5760043561226281610cdc565b6044359067ffffffffffffffff821161000e573660238301121561000e5781600401359061228f826121fb565b9161229d6040519384611d2e565b808352366024828601011161000e5760208160009260246116ca9701838701378401015260243590614ded565b503461000e57600060031936011261000e576020610647614dad565b503461000e576122f536611e42565b6122fd6132a6565b60005b6003548110156123725780612368612340611f6261232061236d95615b42565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff8154169055565b6133ff565b612300565b5061237b615b87565b60005b8181106123c15750816123b2827f40aed8e423b39a56b445ae160f4c071fc2cfb48ee0b6dcd5ffeb6bc5b18d10d094615bda565b610e0a60405192839283615cb7565b6123cf611f5d828486613fdf565b6124036123ff611f888373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b1590565b6124ec5761243e6124348273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5460081c60ff1690565b6124a4579061236861247361249f9373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b6101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff825416179055565b61237e565b6040517ea5216600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b6040517feac13dcd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e57602060031936011261000e5760206106ba60043561255881610cdc565b61496a565b503461000e5761256c36611e42565b906125756132a6565b61257d6159c3565b61258d612588614dad565b613c86565b60005b8281106125b4576116ca611f156125a685614090565b60045460081c60ff1661407c565b6125c2611f5d828585613fdf565b6125f46125ef8273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b614001565b906126026123ff8351151590565b6124ec5760208201516129f6576126df92917f2360404a74478febece1a14f11275f22ada88d19ef96f7d785913010bfff447991612053612656610aa16040809501516bffffffffffffffffffffffff1690565b92836126e4575b6126b261268a8473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055565b51928392836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b612590565b6127f66127ac6126fb6126f686614c08565b6139c1565b61275861271f600a9261271a84546bffffffffffffffffffffffff1690565b613dcf565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a541617600a55565b61278761271f6127726126f661276c61393a565b8b6138d2565b83546bffffffffffffffffffffffff16613dcf565b61271a6127966126f688614d24565b915460601c6bffffffffffffffffffffffff1690565b7fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b61283b61280d61280860085460ff1690565b61404b565b60ff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006008541617600855565b6128906128688473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff8154169055565b61299c610acd6bffffffffffffffffffffffff6128ac876139c1565b61291a6128ce8261271a6004546bffffffffffffffffffffffff9060701c1690565b7fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff79ffffffffffffffffffffffff00000000000000000000000000006004549260701b16911617600455565b612993816129488973ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b907fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff79ffffffffffffffffffffffff000000000000000000000000000083549260701b169116179055565b1660065461356d565b6129f16129c98473ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008154169055565b61265d565b6040517fded6031900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e5760008060031936011261083f57612a5a6159c3565b612a8a610aa1611faa3373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b8015612e0a5760409081517ffeaf968c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9160a082600481867f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419165afa918215612dfd575b85908693612dbd575b5069ffffffffffffffffffff16916010548314612d8c57612b4b7f0000000000000000000000000000000000000000000000000000000000002a308261356d565b4210612d2d57612b7c907f0000000000000000000000000000000000000000000000000000000000002ee09061356d565b4210908180612d56575b612d2d5792827fd2720e8f454493f612cc97499fe8cbce7fa4d4c18d346fe7104e9042df1c1edd612bc6612c21948997612bc1602098601055565b61596b565b875133815260208101939093526040830181905291606090a185517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481019190915293849283919082906044820190565b03927f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca165af18015612d20575b612d02575b50612cae612c5f614dad565b612c67614103565b7f000000000000000000000000000000000000000000000000000000000076a7007f00000000000000000000000000000000000000000000003635c9adc5dea00000614366565b611839612ccb610aa16005546bffffffffffffffffffffffff1690565b612cd3615ab9565b612ceb6110f260095469ffffffffffffffffffff1690565b612cf3614b20565b91612cfc614dad565b936136c4565b612d199060203d81116107f3576107e48183611d2e565b5038612c53565b612d286139f2565b612c4e565b600485517ffc53c50a000000000000000000000000000000000000000000000000000000008152fd5b50612d876123ff611f883373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b612b86565b84517ff3553c2200000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b69ffffffffffffffffffff9350612deb915060a03d8111612df6575b612de38183611d2e565b81019061427a565b50949150612b0a9050565b503d612dd9565b612e056139f2565b612b01565b60046040517fef67f5d8000000000000000000000000000000000000000000000000000000008152fd5b503461000e57600060031936011261000e576020610647614b20565b503461000e57600060031936011261000e57612e6a6132a6565b612e726159c3565b612e7a615ab9565b612f437fffffffffffffffff00000000000000000000000000000000000000000000000077ffffffffffffffffffffffff000000000000000000000000612f73612f27612f21612f1b612ecb614dad565b97612ed589613c86565b612edd613bb5565b888103908111612fd5575b610637612ef361393a565b9169ffffffffffffffffffff60095416948591612f1b8561063764e8d4a51000998a946134e6565b046139c1565b9a6134e6565b600a54956bffffffffffffffffffffffff958691828916613dcf565b1694857fffffffffffffffffffffffffffffffffffffffff00000000000000000000000088161760601c16613dcf565b60601b1692161717600a5542600b557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006004541660045560006040517ff7d0e0f15586495da8c687328ead30fb829d9da55538cb0ef73dd229e517cdb88282a1f35b612fdd6133cf565b612ee8565b503461000e57600060031936011261000e57612ffc6132a6565b600d5473ffffffffffffffffffffffffffffffffffffffff168015610e0f57613026600e5461355d565b4210612e0a576130719073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000600f541617600f55565b61309e7fffffffffffffffffffffffff0000000000000000000000000000000000000000600d5416600d55565b7ffa33c052bbee754f3c0482a89962daffe749191fa33c696a61e947fbfd68bd84610e0a610de3600f5473ffffffffffffffffffffffffffffffffffffffff1690565b503461000e5760008060031936011261083f576130fc6132a6565b613104614b09565b6108075761077e6020613141613118614b20565b600a54906131366bffffffffffffffffffffffff918284169061348b565b9160601c169061348b565b6040518181527f150a6ec0e6f4e9ddcaaaa1674f157d91165a42d60653016f87a9fc870a39f050908060208101610b86565b503461000e57602060031936011261000e5773ffffffffffffffffffffffffffffffffffffffff6004356131a681610cdc565b6131ae6132a6565b1633811461324857806000917fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015561321d613204835473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b90604051917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12788484a3f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff6000541633036132c757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152fd5b90601154801561336c576133699260405173ffffffffffffffffffffffffffffffffffffffff60208201921682526020815261336081611d12565b51902091613374565b90565b505050600190565b929091906000915b84518310156133c75761338f8386613469565b51908181116133b2576000526020526133ac6040600020926133ff565b9161337c565b906000526020526133ac6040600020926133ff565b915092501490565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461342d570190565b6134356133cf565b0190565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602091815181101561347e575b60051b010190565b613486613439565b613476565b9190820391821161349857565b6134a06133cf565b565b64e8d4a5100090807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166134da570290565b6134e26133cf565b0290565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211811515166134da570290565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115613551570490565b613559613517565b0490565b9062093a80820180921161349857565b9190820180921161349857565b929394916135df906135d961358f85856138f5565b956135d36135b061359f88613957565b8b8a829d039081116136b75761348b565b6135c86110f260095469ffffffffffffffffffff1690565b850361366f576134a2565b926134e6565b90613547565b938410613645576136409464e8d4a51000613633866106378661361e61271f6126f661363a9b6117d9876136196127ac9e6126f69e61348b565b6138d2565b69ffffffffffffffffffff60095416906134e6565b049061356d565b4261356d565b600b55565b60046040517fda056d00000000000000000000000000000000000000000000000000000000008152fd5b6136b261367b86613980565b69ffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000006009541617600955565b6134a2565b6136bf6133cf565b61348b565b928491926136d283856138f5565b906137156136df85613957565b96838503948511613826575b878503948511613819575b6135d369ffffffffffffffffffff958660095416850361366f576134a2565b90811561380c575b0461372a8197829661348b565b90613734916138d2565b61373d9161356d565b613746906139c1565b613783906bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a541617600a55565b60095416613790916134e6565b9061379a916134e6565b64e8d4a5100090046137ab9161356d565b6137b4906139c1565b613802907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b613640904261356d565b613814613517565b61371d565b6138216133cf565b6136f6565b61382e6133cf565b6136eb565b6136bf93916135d9613899926135d3600a5469ffffffffffffffffffff61388c6bffffffffffffffffffffffff988984169b8c9a61386f61393a565b506009549560601c169a8b92600b544281116138b6575b5061348b565b9116850361366f576134a2565b918210613645576127ac61363a916126f661271f613640966139c1565b428103116138c5575b38613886565b6138cd6133cf565b6138bf565b64e8d4a51000916106376135599269ffffffffffffffffffff60095416906134e6565b61392564e8d4a51000916bffffffffffffffffffffffff600a541693810390811161065c5761063761062161393a565b0481039081116139325790565b6133696133cf565b600b5442811161394a5750600090565b4281039081116139325790565b64e8d4a510006139256bffffffffffffffffffffffff600a5460601c169261063761062161393a565b69ffffffffffffffffffff90818111613997571690565b60046040517f811752de000000000000000000000000000000000000000000000000000000008152fd5b6bffffffffffffffffffffffff90818111613997571690565b9081602091031261000e5751801515810361000e5790565b506040513d6000823e3d90fd5b600c5463ffffffff949385821661000e5783613b3f93613b3a7f125fc8494f786b470e3c39d0932a62e9e09e291ebd81ea19c57604f6d2b1d16798889569ffffffffffffffffffff613a5086613980565b167fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000060095416176009557fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000613aa442613b84565b928316911617600c556008547fffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffff70ffffffff000000000000000000000000008360681b169116176008557fffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffff79ffffffff000000000000000000000000000000000000000000006009549260b01b16911617600955565b613833565b613b7f613b51600c5463ffffffff1690565b600b5490604051948594859094939263ffffffff906060936080840197845260208401521660408201520152565b0390a1565b63ffffffff90818111613997571690565b9190916bffffffffffffffffffffffff8080941691160191821161349857565b613bc2600b544290613fcd565b600954907fffffffffffff00000000000000000000000000000000ffffffffffffffffffff79ffffffff00000000000000000000000000000000000000000000613c6c75ffffffffffffffffffffffff00000000000000000000613c62613c4b6126f663ffffffff8960b01c168803888111613c79575b69ffffffffffffffffffff8a166134e6565b6bffffffffffffffffffffffff8860501c16613b95565b60501b1693613b84565b60b01b1692161717600955565b613c816133cf565b613c39565b6126f6613c9291613cfd565b6008547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff6cffffffffffffffffffffffff0070ffffffff00000000000000000000000000613ceb613ce6600b544290613fcd565b613b84565b60681b169360081b1691161717600855565b61336990613d0d600b5442101590565b15613d82576bffffffffffffffffffffffff613d7864e8d4a51000613d6b613d4f600b54613d4963ffffffff60085460681c1663ffffffff1690565b9061348b565b6106376008549669ffffffffffffffffffff60095416906134e6565b046135d960ff8516613dbe565b9160081c1661356d565b6bffffffffffffffffffffffff613d7864e8d4a51000613d6b63ffffffff60085460681c16420342811115613d4f57613db96133cf565b613d4f565b60018110613dc95790565b50600190565b6bffffffffffffffffffffffff918216908216039190821161349857565b6001906bffffffffffffffffffffffff80911690811461342d570190565b6127ac906134a092613e1b61393a565b69ffffffffffffffffffff6009541691610637613e5984612f1b85610637613e528a612f1b64e8d4a510009a8b998a9889946134e6565b98876134e6565b9406613ecd575b06613eb8575b613ea7907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000600a54916bffffffffffffffffffffffff938491828516613b95565b1691161780600a5560601c16613b95565b90613ec5613ea791613ded565b919050613e66565b91613ed790613ded565b91613e60565b613f556134a09164e8d4a51000613f1181612f1b613ef961393a565b61063769ffffffffffffffffffff60095416876134e6565b9106613fbf575b600a547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006bffffffffffffffffffffffff80948194828516613b95565b1691161780600a5560601c16908111613fb2577fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff000000000000000000000000600a549260601b16911617600a55565b613fba6133cf565b6127ac565b613fc890613ded565b613f18565b9080821015613fda575090565b905090565b9190811015613fef5760051b0190565b611d91613439565b3561336981610cdc565b9060405161400e81611ce9565b606081935460ff81161515835260ff8160081c16151560208401526bffffffffffffffffffffffff90818160101c16604085015260701c16910152565b60ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9116019060ff821161349857565b9060ff8091169116039060ff821161349857565b60ff81116139975760ff1690565b156140a557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152fd5b60405190600354808352826020918282019060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b936000905b828210614156575050506134a092500383611d2e565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958895509381019390910190614140565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b919260a09373ffffffffffffffffffffffffffffffffffffffff6133699896931684526020840152604083015260608201528160808201520191614186565b60409073ffffffffffffffffffffffffffffffffffffffff61336995931681528160208201520191614186565b613369939273ffffffffffffffffffffffffffffffffffffffff6060931682526020820152816040820152019061050f565b519069ffffffffffffffffffff8216820361000e57565b908160a091031261000e5761428e81614263565b91602082015191604081015191613369608060608401519301614263565b906142b682611d6f565b6142c36040519182611d2e565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06142f18294611d6f565b0190602036910137565b90815180825260208080930193019160005b82811061431b575050505090565b83518552938101939281019260010161430d565b916143589061434a6133699593606086526060860190610c61565b9084820360208601526142fb565b9160408184039101526142fb565b939291909360ff61437960085460ff1690565b1615614522578264e8d4a510006143a6876106376143ad9569ffffffffffffffffffff60095416906134e6565b0495614529565b91600091826143bc82516142ac565b946143c783516142ac565b92855b81518710156144a1576143fa6143e08884613469565b5173ffffffffffffffffffffffffffffffffffffffff1690565b8861442b610aa1611faa8473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b8c811561449257898461447a946144578e61445161448b9a976144859a61445e986145b5565b92613469565b5288614723565b6144688b8a613469565b526144738a8c613469565b519061356d565b946144738988613469565b966133ff565b95926143ca565b50505050929561448b906133ff565b7e635ea9da6e262e92bb713d71840af7c567807ff35bf73e927490c6128324809899506144fd919650613b7f95506127ac9250926126f661271f6144e7614516966139c1565b600a546bffffffffffffffffffffffff16613dcf565b600a5460601c6bffffffffffffffffffffffff16613dcf565b6040519384938461432f565b5050509050565b64e8d4a5100061459960ff92610637604051956060870187811067ffffffffffffffff8211176145a8575b60405263ffffffff60085487811689526bffffffffffffffffffffffff8160081c1660208a015260681c16604088015269ffffffffffffffffffff60095416906134e6565b04915116908115613551570490565b6145b0611cb9565b614554565b916146b061467361336993946145cd600b5442101590565b156146f45761463761462c6145f0600b5463ffffffff60095460b01c169061348b565b955b61462661460e6009549869ffffffffffffffffffff8a166134e6565b6bffffffffffffffffffffffff809960501c1661356d565b906134e6565b64e8d4a51000900490565b90846146638873ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b541682039182116146e757613fcd565b936146a7614680866139c1565b9173ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b92835416613b95565b6bffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000825416179055565b6146ef6133cf565b613fcd565b61463761462c63ffffffff60095460b01c164203428111614716575b956145f2565b61471e6133cf565b614710565b916148066147ed613369939461473b600b5442101590565b1561484e576147ae61479664e8d4a5100061478961476d600b54613d4963ffffffff60085460681c1663ffffffff1690565b6106376008549969ffffffffffffffffffff60095416906134e6565b046135d960ff8816613dbe565b6bffffffffffffffffffffffff809660081c1661356d565b90846147da8873ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5460601c1682039182116146e757613fcd565b936147fa614680866139c1565b92835460601c16613b95565b7fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b6147ae61479664e8d4a5100061478963ffffffff60085460681c1642034281111561476d5761487b6133cf565b61476d565b600b546133699161462c914281116148f45763ffffffff60095460b01c1681039081116148e7575b905b6148d56009549269ffffffffffffffffffff6bffffffffffffffffffffffff8560501c1694166134e6565b8201809211156134e6576106376133cf565b6148ef6133cf565b6148a8565b5063ffffffff60095460b01c164203428111614911575b906148aa565b6149196133cf565b61490b565b9061493d73ffffffffffffffffffffffffffffffffffffffff91613cfd565b911660005260076020526bffffffffffffffffffffffff60406000205460601c1681039081116139325790565b61499a610aa1611faa8373ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b15614b03576149aa6123ff614b09565b614b03576040517ffeaf968c00000000000000000000000000000000000000000000000000000000815260a08160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419165afa908115614af6575b6000908192614ad1575b5069ffffffffffffffffffff601054911614614aca57614a657f0000000000000000000000000000000000000000000000000000000000002a308261356d565b4210614aca57614a96907f0000000000000000000000000000000000000000000000000000000000002ee09061356d565b421015613dc957611f886133699173ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b5050600090565b9050614aeb915060a03d8111612df657612de38183611d2e565b509291505038614a25565b614afe6139f2565b614a1b565b50600090565b60ff6004541680614b175790565b50600b54421090565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca165afa908115614bfb575b600091614bca575b50614ba4615ab9565b8103908111614bbd575b60065481039081116139325790565b614bc56133cf565b614bae565b906020823d8211614bf3575b81614be360209383611d2e565b8101031261083f57505138614b9b565b3d9150614bd6565b614c036139f2565b614b93565b73ffffffffffffffffffffffffffffffffffffffff81169060009180835260026020526bffffffffffffffffffffffff80604085205460101c16938415614cf95760ff604082205416614cd357505050613d49610aa1614cc1614c9a614c9561336996613d497f000000000000000000000000000000000000000000000000000000000000001482613547565b614880565b9373ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b546bffffffffffffffffffffffff1690565b909250614ce260409294614880565b938152600760205220541681039081116139325790565b935050505090565b908015614d17575b810481039081116139325790565b614d1f613517565b614d09565b73ffffffffffffffffffffffffffffffffffffffff81166000526002602052604060002060405190614d5582611ce9565b549060ff821615908115815260ff8360081c161515602082015260606bffffffffffffffffffffffff808560101c169485604085015260701c16910152614aca5715614b035761336990614da7614dad565b9061491e565b6bffffffffffffffffffffffff60045460101c167f0000000000000000000000000000000000000000000000000000000000000014908115613551570490565b9173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca169182330361502657614e3f60ff60015460a01c161561409e565b614e476159c3565b809264e8d4a51000808310614ff05782069182614f3f575b505050614e8f611f888473ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b15614e9e57506134a091615415565b6011549081614eb3575b50506134a0916150ca565b805115612e0a57614f3491614ed4826020806123ff95518301019101615050565b90604051602081019061336081614f088a8591909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611d2e565b612e0a573880614ea8565b829450602091614f54600094614fae9361348b565b956040519485809481937fa9059cbb0000000000000000000000000000000000000000000000000000000083528b600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015614fe3575b614fc5575b8080614e5f565b614fdc9060203d81116107f3576107e48183611d2e565b5038614fbe565b614feb6139f2565b614fb9565b6040517f1d820b1700000000000000000000000000000000000000000000000000000000815264e8d4a510006004820152602490fd5b60046040517f4d695438000000000000000000000000000000000000000000000000000000008152fd5b602090818184031261000e5780519067ffffffffffffffff821161000e57019180601f8401121561000e57825161508681611d6f565b936150946040519586611d2e565b818552838086019260051b82010192831161000e578301905b8282106150bb575050505090565b815181529083019083016150ad565b91906150fc610aa1611faa8573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b92615107828561356d565b937f0000000000000000000000000000000000000000000000000de0b6b3a76400008086106153bc575060055461514c9060601c69ffffffffffffffffffff166110f2565b80861161537857505061515d615ae7565b8083116153455750613b7f7f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909394615196612588614dad565b6152357f000000000000000000000000000000000000000000000000000000000000001461522f6151d06151ca8389613547565b8861348b565b916152296151e06126f685614880565b6146b061520d8a73ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b9161522483546bffffffffffffffffffffffff1690565b613b95565b87613547565b90613e0b565b61529d61525d615244866139c1565b60045460101c6bffffffffffffffffffffffff16613b95565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff6dffffffffffffffffffffffff00006004549260101b16911617600455565b6153126152a9826139c1565b6152d38573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b907fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff6dffffffffffffffffffffffff000083549260101b169116179055565b6040519384938460409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b6040517fb94339d80000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b610bfa916153859161348b565b6040519182917fb94339d8000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b6040517f1d820b170000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b60ff60019116019060ff821161349857565b9060ff8091169116019060ff821161349857565b9190615447610aa1611faa8573ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b92615452828561356d565b937f00000000000000000000000000000000000000000000003635c9adc5dea000008086106153bc575061548b6110f260055460b01c90565b80861161537857507f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909394613b7f911561552d575b6154fc6154cf6126f686614880565b6146b061520d8673ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b6155246128ce61550b866139c1565b60045460701c6bffffffffffffffffffffffff16613b95565b61529d84613edd565b615538612588614dad565b61555361280d60085460ff81169081156155e2575b506153ef565b600880546155dd911c6bffffffffffffffffffffffff166155948573ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b6154c0565b6127ac6bffffffffffffffffffffffff6156159260081c1661271a600a546bffffffffffffffffffffffff9060601c1690565b6156427fffffffffffffffffffffffffffffffffffffff000000000000000000000000ff60085416600855565b3861554d565b6156756125ef8273ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b9160408301926bffffffffffffffffffffffff90816156a086516bffffffffffffffffffffffff1690565b16156158cc576156c1906156b660045460ff1690565b615887575b51151590565b156157ed57506156fc6128ce6156e385516bffffffffffffffffffffffff1690565b60045460701c6bffffffffffffffffffffffff16613dcf565b6157e8610aa16157248461571f610aa188516bffffffffffffffffffffffff1690565b615913565b946157b861286861579161574c610aa16008546bffffffffffffffffffffffff9060081c1690565b613d49610aa161577c8b73ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5460601c6bffffffffffffffffffffffff1690565b9673ffffffffffffffffffffffffffffffffffffffff166000526002602052604060002090565b6157c761271f6144e7886139c1565b6157d66127ac6144fd876139c1565b516bffffffffffffffffffffffff1690565b929190565b836158716128686157918661571f610aa19661583961525d6158206158809b9c9d516bffffffffffffffffffffffff1690565b60045460101c6bffffffffffffffffffffffff16613dcf565b86516bffffffffffffffffffffffff167f00000000000000000000000000000000000000000000000000000000000000149116614d01565b6157d661271f6144e7876139c1565b9190600090565b615892612588614dad565b61589a613bb5565b6158c77fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060045416600455565b6156bb565b6040517fe4adde7200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602490fd5b73ffffffffffffffffffffffffffffffffffffffff64e8d4a5100061594d6bffffffffffffffffffffffff938460095460501c16906134e6565b04921660005260076020526040600020541681039081116139325790565b9061599e576133699060057f00000000000000000000000000000000000000000000017b7883c069166000009104613fcd565b507f00000000000000000000000000000000000000000000017b7883c0691660000090565b6159cb614b09565b156121b457565b604051906159df82611ce9565b81606060045460ff81161515835260ff8160081c1660208401526bffffffffffffffffffffffff90818160101c16604085015260701c16910152565b6004549060ff8260081c1690808210615a825750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060019116176004557fded6ebf04e261e1eb2f3e3b268a2e6aee5b478c15b341eba5cf18b9bc80c2e636000604051a1565b60449250604051917fe709379900000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b615ac16159d2565b6bffffffffffffffffffffffff6060816040840151169201511681018091116139325790565b615aef6159d2565b6005549060406bffffffffffffffffffffffff91615b1c8385169460ff6020840151169060b01c906134e6565b8403938411615b35575b01511681039081116139325790565b615b3d6133cf565b615b26565b600354811015615b7a575b60036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b615b82613439565b615b4d565b60035460008060035581615b99575050565b600381527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b918201915b828110615bcf57505050565b818155600101615bc3565b67ffffffffffffffff8211615caa575b680100000000000000008211615c9d575b60035482600355808310615c5c575b50600360005260005b828110615c1f57505050565b60019060208335615c2f81610cdc565b930192817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b015501615c13565b827fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b91820191015b818110615c915750615c0a565b60008155600101615c84565b615ca5611cb9565b615bfb565b615cb2611cb9565b615bea565b90916040602092828482018583525201929160005b828110615cda575050505090565b90919293828060019273ffffffffffffffffffffffffffffffffffffffff8835615d0381610cdc565b16815201950193929101615ccc56fea164736f6c6343000810000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841900000000000000000000000000000000000000000014adf4b7320334b900000000000000000000000000000000000000000000000000017b7883c06916600000000000000000000000000000000000000000000000000a968163f0a57b4000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002ee000000000000000000000000000000000000000000000017b7883c0691660000000000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000278d00000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000000014
-----Decoded View---------------
Arg [0] : params (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
14 Constructor Arguments found :
Arg [0] : 000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca
Arg [1] : 0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b8419
Arg [2] : 00000000000000000000000000000000000000000014adf4b7320334b9000000
Arg [3] : 00000000000000000000000000000000000000000000017b7883c06916600000
Arg [4] : 000000000000000000000000000000000000000000000a968163f0a57b400000
Arg [5] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [6] : 00000000000000000000000000000000000000000000003635c9adc5dea00000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000002a30
Arg [8] : 0000000000000000000000000000000000000000000000000000000000002ee0
Arg [9] : 00000000000000000000000000000000000000000000017b7883c06916600000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000032
Arg [11] : 0000000000000000000000000000000000000000000000000000000000278d00
Arg [12] : 000000000000000000000000000000000000000000000000000000000076a700
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000014
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.