Latest 25 from a total of 8,324 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Migrate | 24469914 | 13 days ago | IN | 0 ETH | 0.00027453 | ||||
| Migrate | 24469792 | 13 days ago | IN | 0 ETH | 0.00024031 | ||||
| Unstake | 24127312 | 61 days ago | IN | 0 ETH | 0.00000936 | ||||
| Unstake | 24122823 | 61 days ago | IN | 0 ETH | 0.00000972 | ||||
| Migrate | 24074818 | 68 days ago | IN | 0 ETH | 0.00023481 | ||||
| Migrate | 24070504 | 69 days ago | IN | 0 ETH | 0.00013746 | ||||
| Migrate | 24036170 | 74 days ago | IN | 0 ETH | 0.00026954 | ||||
| Migrate | 23961714 | 84 days ago | IN | 0 ETH | 0.00027679 | ||||
| Migrate | 23960550 | 84 days ago | IN | 0 ETH | 0.00026914 | ||||
| Migrate | 23940260 | 87 days ago | IN | 0 ETH | 0.00012219 | ||||
| Migrate | 23925954 | 89 days ago | IN | 0 ETH | 0.00023839 | ||||
| Migrate | 23899477 | 93 days ago | IN | 0 ETH | 0.00023624 | ||||
| Migrate | 23896959 | 93 days ago | IN | 0 ETH | 0.00007217 | ||||
| Migrate | 23884777 | 95 days ago | IN | 0 ETH | 0.0002503 | ||||
| Migrate | 23876399 | 96 days ago | IN | 0 ETH | 0.00027442 | ||||
| Migrate | 23777223 | 110 days ago | IN | 0 ETH | 0.00038744 | ||||
| Migrate | 23501259 | 148 days ago | IN | 0 ETH | 0.00012785 | ||||
| Migrate | 23499074 | 149 days ago | IN | 0 ETH | 0.00019305 | ||||
| Migrate | 23363415 | 168 days ago | IN | 0 ETH | 0.00015929 | ||||
| Migrate | 23246754 | 184 days ago | IN | 0 ETH | 0.00003738 | ||||
| Migrate | 23238449 | 185 days ago | IN | 0 ETH | 0.00012073 | ||||
| Migrate | 23210675 | 189 days ago | IN | 0 ETH | 0.00016125 | ||||
| Migrate | 23198268 | 191 days ago | IN | 0 ETH | 0.00060102 | ||||
| Migrate | 23126439 | 201 days ago | IN | 0 ETH | 0.00032888 | ||||
| Migrate | 23112373 | 203 days ago | IN | 0 ETH | 0.00005263 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 18671931 | 824 days ago | 0.00707205 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
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
Contract ABI
API[{"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):
Arg [1] : LINKAddress (address): 0x514910771AF9Ca656af840dff83E8264EcF986CA
Arg [2] : monitoredFeed (address): 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
Arg [3] : initialMaxPoolSize (uint256): 25000000000000000000000000
Arg [4] : initialMaxCommunityStakeAmount (uint256): 7000000000000000000000
Arg [5] : initialMaxOperatorStakeAmount (uint256): 50000000000000000000000
Arg [6] : minCommunityStakeAmount (uint256): 1000000000000000000
Arg [7] : minOperatorStakeAmount (uint256): 1000000000000000000000
Arg [8] : priorityPeriodThreshold (uint256): 10800
Arg [9] : regularPeriodThreshold (uint256): 12000
Arg [10] : maxAlertingRewardAmount (uint256): 7000000000000000000000
Arg [11] : minInitialOperatorCount (uint256): 50
Arg [12] : minRewardDuration (uint256): 2592000
Arg [13] : slashableDuration (uint256): 7776000
Arg [14] : delegationRateDenominator (uint256): 20
-----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
Net Worth in USD
$4,037,650.74
Net Worth in ETH
2,049.563157
Token Allocations
LINK
100.00%
USDC
0.00%
AXOME
0.00%
Others
0.00%
Multichain Portfolio | 33 Chains
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.