Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
Value | ||||
---|---|---|---|---|---|---|---|---|---|
0xe30b35dc8a46800e8ab06d7e1087473a4289abc309dc757ae13d578234c9b265 | Claim Reward | (pending) | 6 mins ago | IN | 0 ETH | (Pending) | |||
0x003661ea8830d99014557568af6424faaecc1cafbb1ad08f4082f539e9fae537 | Claim Reward | (pending) | 14 mins ago | IN | 0 ETH | (Pending) | |||
Claim Reward | 19782558 | 6 mins ago | IN | 0 ETH | 0.00148503 | ||||
Claim Reward | 19782522 | 14 mins ago | IN | 0 ETH | 0.00177431 | ||||
Claim Reward | 19781925 | 2 hrs ago | IN | 0 ETH | 0.00071851 | ||||
Claim Reward | 19781341 | 4 hrs ago | IN | 0 ETH | 0.00069263 | ||||
Claim Reward | 19781337 | 4 hrs ago | IN | 0 ETH | 0.00063508 | ||||
Claim Reward | 19781335 | 4 hrs ago | IN | 0 ETH | 0.00061635 | ||||
Claim Reward | 19781307 | 4 hrs ago | IN | 0 ETH | 0.00060669 | ||||
Claim Reward | 19781111 | 4 hrs ago | IN | 0 ETH | 0.00071317 | ||||
Claim Reward | 19781097 | 5 hrs ago | IN | 0 ETH | 0.00057577 | ||||
Claim Reward | 19781087 | 5 hrs ago | IN | 0 ETH | 0.000599 | ||||
Claim Reward | 19781077 | 5 hrs ago | IN | 0 ETH | 0.00067798 | ||||
Claim Reward | 19780915 | 5 hrs ago | IN | 0 ETH | 0.00067163 | ||||
Claim Reward | 19780094 | 8 hrs ago | IN | 0 ETH | 0.00058384 | ||||
Claim Reward | 19780079 | 8 hrs ago | IN | 0 ETH | 0.00058318 | ||||
Claim Reward | 19779794 | 9 hrs ago | IN | 0 ETH | 0.00067197 | ||||
Claim Reward | 19779782 | 9 hrs ago | IN | 0 ETH | 0.00074441 | ||||
Claim Reward | 19779186 | 11 hrs ago | IN | 0 ETH | 0.00046253 | ||||
Claim Reward | 19778841 | 12 hrs ago | IN | 0 ETH | 0.00039612 | ||||
Claim Reward | 19778460 | 13 hrs ago | IN | 0 ETH | 0.00045027 | ||||
Claim Reward | 19778449 | 13 hrs ago | IN | 0 ETH | 0.00063542 | ||||
Claim Reward | 19778313 | 14 hrs ago | IN | 0 ETH | 0.00048569 | ||||
Claim Reward | 19778300 | 14 hrs ago | IN | 0 ETH | 0.00057051 | ||||
Claim Reward | 19778250 | 14 hrs ago | IN | 0 ETH | 0.00053029 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
RewardVault
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; import {TypeAndVersionInterface} from "@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol"; import {IRewardVault} from "../interfaces/IRewardVault.sol"; import {IStakingPool} from "../interfaces/IStakingPool.sol"; import {PausableWithAccessControl} from "../PausableWithAccessControl.sol"; import {CommunityStakingPool} from "../pools/CommunityStakingPool.sol"; import {OperatorStakingPool} from "../pools/OperatorStakingPool.sol"; /// @notice This contract is the reward vault for the staking pools. Admin can deposit rewards into /// the vault and set the aggregate reward rate for each pool to control the reward distribution. /// @dev This contract interacts with the community and operator staking pools that it is connected /// to. A reward vault is connected to only one community and operator staking pool during its /// lifetime, which means when we upgrade either one of the pools or introduce a new type of pool, /// we will need to update this contract and deploy a new reward vault. /// @dev invariant LINK balance of the contract is greater than or equal to the sum of unvested /// rewards. /// @dev invariant The sum of all stakers' rewards is less than or equal to the sum of available /// rewards. /// @dev invariant The reward bucket with zero aggregate reward rate has zero reward. /// @dev invariant Stakers' multipliers are within 0 and the max value. /// @dev We only support LINK token in v0.2 staking. Rebasing tokens, ERC777 tokens, fee-on-transfer /// tokens or tokens that do not have 18 decimal places are not supported. contract RewardVault is IRewardVault, PausableWithAccessControl, TypeAndVersionInterface { using FixedPointMathLib for uint256; using SafeCast for uint256; /// @notice This error is thrown when the pool address is not one of the registered staking pools error InvalidPool(); /// @notice This error is thrown when the reward amount is invalid when adding rewards error InvalidRewardAmount(); /// @notice This error is thrown when the aggregate reward rate is invalid when adding rewards error InvalidEmissionRate(); /// @notice This error is thrown when the delegation rate is invalid when setting delegation rate error InvalidDelegationRate(); /// @notice This error is thrown when an address who doesn't have access tries to call a function /// For example, when the caller is not a rewarder and adds rewards to the vault, or /// when the caller is not a staking pool and tries to call updateRewardPerToken. error AccessForbidden(); /// @notice This error is thrown whenever a zero-address is supplied when /// a non-zero address is required error InvalidZeroAddress(); /// @notice This error is thrown when the reward duration is too short when adding rewards error RewardDurationTooShort(); /// @notice this error is thrown when the rewards remaining are insufficient for the new /// delegation rate error InsufficentRewardsForDelegationRate(); /// @notice This error is thrown when calling an operation that is not allowed when the vault is /// closed. error VaultAlreadyClosed(); /// @notice This error is thrown when the staker tries to claim rewards and the staker has no /// rewards to claim. error NoRewardToClaim(); /// @notice This event is emitted when the delegation rate is updated. /// @param oldDelegationRate The old delegationRate /// @param newDelegationRate The new delegationRate event DelegationRateSet(uint256 oldDelegationRate, uint256 newDelegationRate); /// @notice This event is emitted when rewards are added to the vault /// @param pool The pool to which the rewards are added /// @param amount The reward amount /// @param emissionRate The target aggregate reward rate (token/second) event RewardAdded(address indexed pool, uint256 amount, uint256 emissionRate); /// @notice This event is emitted when the vault is opened. event VaultOpened(); /// @notice This event is emitted when the vault is closed. /// @param totalUnvestedRewards The total amount of unvested rewards at the /// time the vault was closed event VaultClosed(uint256 totalUnvestedRewards); /// @notice This event is emitted when the staker claims rewards event RewardClaimed(address indexed staker, uint256 claimedRewards); /// @notice This event is emitted when the forfeited rewards are shared back into the reward /// buckets. /// @param vestedReward The amount of forfeited rewards shared in juels /// @param vestedRewardPerToken The amount of forfeited rewards per token added. /// @param reclaimedReward The amount of forfeited rewards reclaimed. /// @param isOperatorReward True if the forfeited reward is from the operator staking pool. event ForfeitedRewardDistributed( uint256 vestedReward, uint256 vestedRewardPerToken, uint256 reclaimedReward, bool isOperatorReward ); /// @notice This event is emitted when the community pool rewards are updated /// @param baseRewardPerToken The per-token base reward of the community staking pool /// pool event CommunityPoolRewardUpdated(uint256 baseRewardPerToken); /// @notice This event is emitted when the operator pool rewards are updated /// @param baseRewardPerToken The per-token base reward of the operator staking pool /// @param delegatedRewardPerToken The per-token delegated reward of the operator staking /// pool event OperatorPoolRewardUpdated(uint256 baseRewardPerToken, uint256 delegatedRewardPerToken); /// @notice This event is emitted when a staker's rewards are updated /// @param staker The staker address /// @param vestedBaseReward The staker's vested base rewards /// @param vestedDelegatedReward The staker's vested delegated rewards /// @param baseRewardPerToken The staker's base reward per token /// @param operatorDelegatedRewardPerToken The staker's delegated reward per token /// @param claimedBaseRewardsInPeriod The staker's claimed base rewards in the period event StakerRewardUpdated( address indexed staker, uint256 vestedBaseReward, uint256 vestedDelegatedReward, uint256 baseRewardPerToken, uint256 operatorDelegatedRewardPerToken, uint256 claimedBaseRewardsInPeriod ); /// @notice This event is emitted when the staker rewards are finalized /// @param staker The staker address /// @param shouldForfeit True if the staker forfeited their rewards event RewardFinalized(address indexed staker, bool shouldForfeit); /// @notice The constructor parameters. struct ConstructorParams { /// @notice The LINK token. LinkTokenInterface linkToken; /// @notice The community staking pool. CommunityStakingPool communityStakingPool; /// @notice The operator staking pool. OperatorStakingPool operatorStakingPool; /// @notice The delegation rate expressed in basis points. For example, a delegation rate of /// 4.5% would be represented as 450 basis points. uint32 delegationRate; /// @notice The time it takes for a multiplier to reach its max value in seconds. uint32 multiplierDuration; /// @notice The time it requires to transfer admin role uint48 adminRoleTransferDelay; } /// @notice This struct is used to store the reward information for a reward bucket. struct RewardBucket { /// @notice The reward aggregate reward rate of the reward bucket in Juels/second. uint80 emissionRate; /// @notice The timestamp when the reward duration ends. uint80 rewardDurationEndsAt; /// @notice The last updated available reward per token of the reward bucket. /// This value only increases over time as more rewards vest to the /// stakers. uint80 vestedRewardPerToken; } /// @notice This struct is used to store the reward buckets states. struct RewardBuckets { /// @notice The reward bucket for the operator staking pool. RewardBucket operatorBase; /// @notice The reward bucket for the community staking pool. RewardBucket communityBase; /// @notice The reward bucket for the delegated rewards. RewardBucket operatorDelegated; } /// @notice This struct is used to store the vault config. struct VaultConfig { /// @notice The delegation rate expressed in basis points. For example, a delegation rate of /// 4.5% would be represented as 450 basis points. uint32 delegationRate; /// @notice Flag that signals if the reward vault is open bool isOpen; } /// @notice This struct is used to store the checkpoint information at the time the reward vault /// is closed struct VestingCheckpointData { /// @notice The total staked LINK amount of the operator staking pool at the time /// the reward vault was closed uint256 operatorPoolTotalPrincipal; /// @notice The total staked LINK amount of the community staking pool at the time /// the reward vault was closed uint256 communityPoolTotalPrincipal; /// @notice The block number of at the time the reward vault was migrated or closed uint256 finalBlockNumber; } /// @notice This struct is used for aggregating the return values of a function that calculates /// the reward aggregate reward rate splits. struct BucketRewardEmissionSplit { /// @notice The reward for the community staking pool uint256 communityReward; /// @notice The reward for the operator staking pool uint256 operatorReward; /// @notice The reward for the delegated staking pool uint256 operatorDelegatedReward; /// @notice The aggregate reward rate for the community staking pool uint256 communityRate; /// @notice The aggregate reward rate for the operator staking pool uint256 operatorRate; /// @notice The aggregate reward rate for the delegated staking pool uint256 delegatedRate; } /// @notice This is the ID for the rewarder role, which is given to the /// addresses that will add rewards to the vault. /// @dev Hash: beec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f6 bytes32 public constant REWARDER_ROLE = keccak256("REWARDER_ROLE"); /// @notice The maximum possible value of a multiplier. Current implementation requires that this /// value is 1e18 (i.e. 100%). uint256 private constant MAX_MULTIPLIER = 1e18; /// @notice The denominator used to calculate the delegation rate. uint256 private constant DELEGATION_BASIS_POINTS_DENOMINATOR = 10000; /// @notice The multiplier ramp up period duration in seconds. uint256 private immutable i_multiplierDuration; /// @notice The LINK token LinkTokenInterface private immutable i_LINK; /// @notice The community staking pool. CommunityStakingPool private immutable i_communityStakingPool; /// @notice The operator staking pool. OperatorStakingPool private immutable i_operatorStakingPool; /// @notice The reward buckets. RewardBuckets private s_rewardBuckets; /// @notice The vault config. VaultConfig private s_vaultConfig; /// @notice The checkpoint information at the time the reward vault was closed VestingCheckpointData private s_finalVestingCheckpointData; /// @notice The packed timestamps of reward updates. First digits contain community reward /// update timestamp and last 18 digits contain operator timestamp, e.g., if both timestamps are /// 1_697_127_483_832 then the value would be 1_697_127_483_832_000_001_697_127_483_832. uint256 private s_packedRewardUpdateTimestamps; /// @notice Stores reward information for each staker mapping(address => StakerReward) private s_rewards; constructor(ConstructorParams memory params) PausableWithAccessControl(params.adminRoleTransferDelay, msg.sender) { if (address(params.linkToken) == address(0)) revert InvalidZeroAddress(); if (address(params.communityStakingPool) == address(0)) revert InvalidZeroAddress(); if (address(params.operatorStakingPool) == address(0)) revert InvalidZeroAddress(); if (params.delegationRate > DELEGATION_BASIS_POINTS_DENOMINATOR) revert InvalidDelegationRate(); i_multiplierDuration = params.multiplierDuration; i_LINK = params.linkToken; i_communityStakingPool = params.communityStakingPool; i_operatorStakingPool = params.operatorStakingPool; s_vaultConfig.delegationRate = params.delegationRate; emit DelegationRateSet(0, params.delegationRate); s_vaultConfig.isOpen = true; emit VaultOpened(); } /// @notice Adds more rewards into the reward vault /// Calculates the reward duration from the amount and aggregate reward rate /// @dev To add rewards to all pools use address(0) as the pool address /// @dev There is a possibility that a fraction of the added rewards can be locked in this /// contract as dust, specifically, when the amount is not divided by the aggregate reward rate /// evenly. We /// will handle this case operationally and make sure that the amount is large relative to the /// aggregate reward rate so there will only be small dust (less than 10^18 juels). /// @param pool The staking pool address /// @param amount The reward amount /// @param emissionRate The target aggregate reward rate (token/second) /// @dev precondition The caller must have the REWARDER role. /// @dev precondition This contract must be open and not paused. /// @dev precondition The caller must have at least `amount` LINK tokens. /// @dev precondition The caller must have approved this contract for the transfer of at least /// `amount` LINK tokens. function addReward( address pool, uint256 amount, uint256 emissionRate ) external onlyRewarder whenOpen whenNotPaused { // check if the pool is either community staking pool or operator staking pool // if the pool is the zero address, then the reward is split between all pools if ( pool != address(0) && pool != address(i_communityStakingPool) && pool != address(i_operatorStakingPool) ) { revert InvalidPool(); } // check that the aggregate reward rate is greater than zero if (emissionRate == 0) revert InvalidEmissionRate(); // update the reward per tokens _updateRewardPerToken(); // update the reward buckets _updateRewardBuckets({pool: pool, amount: amount, emissionRate: emissionRate}); // transfer the reward tokens to the reward vault // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transferFrom({from: msg.sender, to: address(this), value: amount}); emit RewardAdded(pool, amount, emissionRate); } /// @notice Returns the delegation rate /// @return The delegation rate expressed in basis points function getDelegationRate() external view returns (uint256) { return s_vaultConfig.delegationRate; } /// @notice Updates the delegation rate /// @param newDelegationRate The delegation rate. /// @dev precondition The caller must have the default admin role. function setDelegationRate(uint256 newDelegationRate) external onlyRole(DEFAULT_ADMIN_ROLE) { uint256 oldDelegationRate = s_vaultConfig.delegationRate; if ( oldDelegationRate == newDelegationRate || newDelegationRate > DELEGATION_BASIS_POINTS_DENOMINATOR ) { revert InvalidDelegationRate(); } uint256 communityRateWithoutDelegation = s_rewardBuckets.communityBase.emissionRate + s_rewardBuckets.operatorDelegated.emissionRate; uint256 delegatedRate = newDelegationRate == 0 ? 0 : communityRateWithoutDelegation * newDelegationRate / DELEGATION_BASIS_POINTS_DENOMINATOR; if (delegatedRate == 0 && newDelegationRate != 0 && communityRateWithoutDelegation != 0) { // delegated rate has rounded down to zero revert InsufficentRewardsForDelegationRate(); } _updateRewardPerToken(); uint256 unvestedRewards = _getUnvestedRewards(s_rewardBuckets.communityBase) + _getUnvestedRewards(s_rewardBuckets.operatorDelegated); uint256 communityRate = communityRateWithoutDelegation - delegatedRate; s_rewardBuckets.communityBase.emissionRate = communityRate.toUint80(); s_rewardBuckets.operatorDelegated.emissionRate = delegatedRate.toUint80(); // NOTE - the reward duration for both buckets need to be in sync. if (newDelegationRate == 0) { delete s_rewardBuckets.operatorDelegated.rewardDurationEndsAt; _updateRewardDurationEndsAt({ bucket: s_rewardBuckets.communityBase, rewardAmount: unvestedRewards, emissionRate: communityRate }); } else if (newDelegationRate == DELEGATION_BASIS_POINTS_DENOMINATOR) { delete s_rewardBuckets.communityBase.rewardDurationEndsAt; _updateRewardDurationEndsAt({ bucket: s_rewardBuckets.operatorDelegated, rewardAmount: unvestedRewards, emissionRate: delegatedRate }); } else if (unvestedRewards != 0) { uint256 delegatedRewards = unvestedRewards * newDelegationRate / DELEGATION_BASIS_POINTS_DENOMINATOR; uint256 communityRewards = unvestedRewards - delegatedRewards; _updateRewardDurationEndsAt({ bucket: s_rewardBuckets.communityBase, rewardAmount: communityRewards, emissionRate: communityRate }); _updateRewardDurationEndsAt({ bucket: s_rewardBuckets.operatorDelegated, rewardAmount: delegatedRewards, emissionRate: delegatedRate }); } s_vaultConfig.delegationRate = newDelegationRate.toUint32(); emit DelegationRateSet(oldDelegationRate, newDelegationRate); } // ================= // IRewardVault // ================= /// @inheritdoc IRewardVault /// @dev precondition This contract must not be paused. /// @dev precondition The caller must be a staker with a non-zero reward. function claimReward() external whenNotPaused returns (uint256) { bool isOperator = _isOperator(msg.sender); _updateRewardPerToken(isOperator ? StakerType.OPERATOR : StakerType.COMMUNITY); IStakingPool stakingPool = isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool); uint256 stakerPrincipal = _getStakerPrincipal(msg.sender, stakingPool); StakerReward memory stakerReward = _calculateStakerReward({ staker: msg.sender, isOperator: isOperator, stakerPrincipal: stakerPrincipal }); uint112 newVestedBaseRewards = _calculateNewVestedBaseRewards( stakerReward, _getMultiplier(_getStakerStakedAtTime(msg.sender, stakingPool)) ); stakerReward.unvestedBaseReward -= newVestedBaseRewards; stakerReward.claimedBaseRewardsInPeriod += newVestedBaseRewards; uint256 newVestedRewards = stakerReward.vestedBaseReward + newVestedBaseRewards; delete stakerReward.vestedBaseReward; if (isOperator) { newVestedRewards += stakerReward.vestedDelegatedReward; delete stakerReward.vestedDelegatedReward; } if (newVestedRewards == 0) { revert NoRewardToClaim(); } s_rewards[msg.sender] = stakerReward; // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transfer(msg.sender, newVestedRewards); emit RewardClaimed(msg.sender, newVestedRewards); emit StakerRewardUpdated( msg.sender, 0, 0, stakerReward.baseRewardPerToken, stakerReward.operatorDelegatedRewardPerToken, stakerReward.claimedBaseRewardsInPeriod ); return newVestedRewards; } /// @inheritdoc IRewardVault /// @dev precondition The caller must be a staking pool. function updateReward(address staker, uint256 stakerPrincipal) external onlyStakingPool { _updateRewardPerToken(); StakerReward memory stakerReward = _calculateStakerReward({ staker: staker, isOperator: msg.sender == address(i_operatorStakingPool), stakerPrincipal: stakerPrincipal }); s_rewards[staker] = stakerReward; emit StakerRewardUpdated( staker, stakerReward.vestedBaseReward, stakerReward.vestedDelegatedReward, stakerReward.baseRewardPerToken, stakerReward.operatorDelegatedRewardPerToken, stakerReward.claimedBaseRewardsInPeriod ); } /// @inheritdoc IRewardVault /// @dev This applies any final logic such as the multipliers to the staker's newly accrued and /// stored rewards and store the value. /// @dev The caller staking pool must update the total staked LINK amount of the pool AFTER /// calling this /// function. /// @dev precondition The caller must be a staking pool. function concludeRewardPeriod( address staker, uint256 oldPrincipal, uint256 stakedAt, uint256 unstakedAmount, bool shouldForfeit ) external onlyStakingPool { // _isOperator is not used here to save gas. The _isOperator function // currently checks for 2 things. The first that the staker is currently // an operator and the other is that the staker is a removed operator. As // this function will only be called by a staking pool, the contract can // safely assume that the staker is an operator if the msg.sender is the // operator staking pool as upgrading a pool/reward vault means that the operator // staking pool will point to a new reward vault. Additionally the contract // assumes that it does not need to do the second check to determine whether // or not an operator had been removed as it is unlikely that an operator // is removed after the reward vault is closed. bool isOperator = msg.sender == address(i_operatorStakingPool); _updateRewardPerToken(isOperator ? StakerType.OPERATOR : StakerType.COMMUNITY); StakerReward memory stakerReward = _calculateStakerReward({ staker: staker, isOperator: isOperator, stakerPrincipal: oldPrincipal }); uint112 newVestedBaseRewards = _calculateNewVestedBaseRewards(stakerReward, _getMultiplier(stakedAt)); stakerReward.unvestedBaseReward -= newVestedBaseRewards; stakerReward.vestedBaseReward += newVestedBaseRewards; // claimedBaseRewardsInPeriod is reset as this function ends a // reward period for the staker. This variable only tracks the amount // of rewards a staker has claimed within a period hence should only // accumulate from zero after this function is called. delete stakerReward.claimedBaseRewardsInPeriod; if (!shouldForfeit) { return _storeRewardAndEmitEvents(staker, stakerReward, shouldForfeit); } uint112 unvestedRewardAmount = stakerReward.unvestedBaseReward; // The function terminates here as a staker that has reached the maximum // multiplier will not have any unvested rewards hence will not forfeit // anything. if (unvestedRewardAmount == 0) { return _storeRewardAndEmitEvents(staker, stakerReward, shouldForfeit); } IStakingPool stakingPool = isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool); uint256 remainingPoolPrincipal = _getTotalPrincipal(stakingPool) - oldPrincipal; // This is the case when the last staker exits the pool. if (remainingPoolPrincipal == 0) { delete stakerReward.unvestedBaseReward; stakerReward.vestedBaseReward += unvestedRewardAmount; emit ForfeitedRewardDistributed(0, 0, unvestedRewardAmount, isOperator); return _storeRewardAndEmitEvents(staker, stakerReward, shouldForfeit); } // This handles an edge case when an operator with 0 principal remaining (due to // slashing) gets removed and forfeits rewards. In this scenario, the reward vault will // forfeit the full amount of unclaimable rewards instead of calculating // the proportion of the unclaimable rewards that should be forfeited. // There is another case when forfeitedRewardAmount rounds down to 0, which is when a staker has // earned too little rewards and unstakes a very small amount. In this case, we do not forfeit // any rewards. uint256 forfeitedRewardAmount = oldPrincipal == 0 ? unvestedRewardAmount : unvestedRewardAmount * unstakedAmount / oldPrincipal; RewardBucket storage rewardBucket = isOperator ? s_rewardBuckets.operatorBase : s_rewardBuckets.communityBase; uint256 redistributedRewardPerToken = forfeitedRewardAmount.divWadDown(remainingPoolPrincipal); /// There is an extreme edge case where redistributedRewardPerToken may overflow /// because the remaining principal in a pool is an extremely small amount. /// This scenario is however extremely unlikely because there is a minimum /// staked amount for both the operator and community staking pools. /// Operators may be slashed so that the sum of remaining staked amounts /// is extremely small but this scenario is also unlikely to happen as /// it would mean multiple CL services going down at the same time. rewardBucket.vestedRewardPerToken += redistributedRewardPerToken.toUint80(); emit ForfeitedRewardDistributed( forfeitedRewardAmount, redistributedRewardPerToken, 0, isOperator ); // Update stakerRewardPerToken so that the staker doesn't benefit from redistributed // tokens _updateStakerRewardPerToken(stakerReward, isOperator); stakerReward.unvestedBaseReward -= forfeitedRewardAmount.toUint112(); return _storeRewardAndEmitEvents(staker, stakerReward, shouldForfeit); } /// @notice Updates a staker's reward data and emits events /// @param staker The address of the staker to update reward data for /// @param stakerReward The staker's new reward data /// @param shouldForfeit True if the staker has forfeited some unvested /// rewards function _storeRewardAndEmitEvents( address staker, StakerReward memory stakerReward, bool shouldForfeit ) internal { s_rewards[staker] = stakerReward; emit RewardFinalized(staker, shouldForfeit); emit StakerRewardUpdated( staker, stakerReward.vestedBaseReward, stakerReward.vestedDelegatedReward, stakerReward.baseRewardPerToken, stakerReward.operatorDelegatedRewardPerToken, stakerReward.claimedBaseRewardsInPeriod ); } /// @notice Calculates new vested base rewards, taking into account the multiplier /// and the rewards that have already been claimed. /// @return New vested base rewards function _calculateNewVestedBaseRewards( StakerReward memory stakerReward, uint256 multiplier ) internal pure returns (uint112) { return uint256(stakerReward.unvestedBaseReward + stakerReward.claimedBaseRewardsInPeriod) .mulWadDown(multiplier).toUint112() - stakerReward.claimedBaseRewardsInPeriod; } /// @inheritdoc IRewardVault /// @dev Withdraws any unvested LINK rewards to the owner's address. /// @dev precondition The caller must have the default admin role. /// @dev precondition This contract must be open. function close() external onlyRole(DEFAULT_ADMIN_ROLE) whenOpen { (, uint256 totalUnvestedRewards,,,) = _stopVestingRewardsToBuckets(); delete s_vaultConfig.isOpen; // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transfer(msg.sender, totalUnvestedRewards); emit VaultClosed(totalUnvestedRewards); } /// @inheritdoc IRewardVault function getReward(address staker) external view returns (uint256) { // Determine if staker is operator or community bool isOperator = _isOperator(staker); IStakingPool stakingPool = isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool); uint256 stakerPrincipal = _getStakerPrincipal(staker, stakingPool); (StakerReward memory stakerReward, uint256 forfeitedReward) = _getReward(staker, stakerPrincipal, isOperator); (,, uint256 reclaimableReward) = _calculateForfeitedRewardDistribution( forfeitedReward, _getTotalPrincipal(stakingPool) - stakerPrincipal ); return stakerReward.vestedBaseReward + stakerReward.vestedDelegatedReward + reclaimableReward; } /// @inheritdoc IRewardVault function isOpen() external view returns (bool) { return s_vaultConfig.isOpen; } /// @inheritdoc IRewardVault function hasRewardDurationEnded(address stakingPool) external view returns (bool) { if (stakingPool == address(i_operatorStakingPool)) { return s_rewardBuckets.operatorBase.rewardDurationEndsAt <= block.timestamp && s_rewardBuckets.operatorDelegated.rewardDurationEndsAt <= block.timestamp; } if (stakingPool == address(i_communityStakingPool)) { return s_rewardBuckets.communityBase.rewardDurationEndsAt <= block.timestamp; } revert InvalidPool(); } /// @inheritdoc IRewardVault function hasRewardAdded() external view returns (bool) { return s_rewardBuckets.operatorBase.emissionRate != 0 || s_rewardBuckets.communityBase.emissionRate != 0 || s_rewardBuckets.operatorDelegated.emissionRate != 0; } /// @inheritdoc IRewardVault function getStoredReward(address staker) external view returns (StakerReward memory) { return s_rewards[staker]; } /// @notice Returns the reward buckets within this vault /// @return The reward buckets function getRewardBuckets() external view returns (RewardBuckets memory) { return s_rewardBuckets; } /// @notice Returns the timestamp of the last reward per token update /// @return uint256 communityRewardUpdateTimestamp The timestamp of the last update /// @return uint256 operatorRewardUpdateTimestamp The timestamp of the last update function getRewardPerTokenUpdatedAt() external view returns (uint256, uint256) { return _getRewardUpdateTimestamps(s_packedRewardUpdateTimestamps); } /// @notice Returns the multiplier ramp up time /// @return uint256 The multiplier ramp up time function getMultiplierDuration() external view returns (uint256) { return i_multiplierDuration; } /// @notice Returns the ramp up multiplier of the staker /// @dev Multipliers are in the range of 0 and 1, so we multiply them by 1e18 (WAD) to preserve /// the decimals. /// @param staker The address of the staker /// @return uint256 The staker's multiplier function getMultiplier(address staker) external view returns (uint256) { IStakingPool stakingPool = _isOperator(staker) ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool); return _getMultiplier(_getStakerStakedAtTime(staker, stakingPool)); } /// @notice Calculates and returns the latest reward info of the staker /// @param staker The staker address /// @return StakerReward The staker's reward info /// @return uint256 The staker's forfeited reward in juels function calculateLatestStakerReward(address staker) external view returns (StakerReward memory, uint256) { // Determine if staker is operator or community bool isOperator = _isOperator(staker); IStakingPool stakingPool = isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool); uint256 stakerPrincipal = _getStakerPrincipal(staker, stakingPool); return _getReward(staker, stakerPrincipal, isOperator); } /// @notice Returns the final checkpoint data /// @return VestingCheckpointData The final checkpoint function getFinalVestingCheckpointData() external view returns (VestingCheckpointData memory) { return s_finalVestingCheckpointData; } /// @notice Returns the unvested rewards /// @return unvestedCommunityBaseRewards The unvested community base rewards /// @return unvestedOperatorBaseRewards The unvested operator base rewards /// @return unvestedOperatorDelegatedRewards The unvested operator delegated rewards function getUnvestedRewards() external view returns (uint256, uint256, uint256) { uint256 unvestedCommunityBaseRewards = _getUnvestedRewards(s_rewardBuckets.communityBase); uint256 unvestedOperatorBaseRewards = _getUnvestedRewards(s_rewardBuckets.operatorBase); uint256 unvestedOperatorDelegatedRewards = _getUnvestedRewards(s_rewardBuckets.operatorDelegated); return (unvestedCommunityBaseRewards, unvestedOperatorBaseRewards, unvestedOperatorDelegatedRewards); } /// @inheritdoc IRewardVault function isPaused() external view returns (bool) { return paused(); } /// @inheritdoc IRewardVault function getStakingPools() external view override returns (address[] memory) { address[] memory stakingPools = new address[](2); stakingPools[0] = address(i_operatorStakingPool); stakingPools[1] = address(i_communityStakingPool); return stakingPools; } // ========= // Helpers // ========= /// @notice Stops rewards in all buckets from vesting and close the vault. /// @dev This will also checkpoint the staking pools /// @return uint256 The total aggregate reward rate from all three buckets /// @return uint256 The total amount of available rewards in juels /// @return uint256 The amount of available operator base rewards in juels /// @return uint256 The amount of available community base rewards in juels /// @return uint256 The amount of available operator delegated rewards in juels function _stopVestingRewardsToBuckets() private returns (uint256, uint256, uint256, uint256, uint256) { _updateRewardPerToken(); uint256 unvestedOperatorBaseRewards = _stopVestingBucketRewards(s_rewardBuckets.operatorBase); uint256 unvestedCommunityBaseRewards = _stopVestingBucketRewards(s_rewardBuckets.communityBase); uint256 unvestedOperatorDelegatedRewards = _stopVestingBucketRewards(s_rewardBuckets.operatorDelegated); uint256 totalUnvestedRewards = unvestedOperatorBaseRewards + unvestedCommunityBaseRewards + unvestedOperatorDelegatedRewards; _checkpointStakingPools(); return ( s_rewardBuckets.operatorBase.emissionRate + s_rewardBuckets.communityBase.emissionRate + s_rewardBuckets.operatorDelegated.emissionRate, totalUnvestedRewards, unvestedOperatorBaseRewards, unvestedCommunityBaseRewards, unvestedOperatorDelegatedRewards ); } /// @notice Returns the total staked LINK amount staked in a staking pool. This will /// return the staking pool's latest total staked LINK amount if the vault has not been /// closed and the pool's total staked LINK amount at the time the vault was /// closed if the vault has already been closed. /// @param stakingPool The staking pool to query the total staked LINK amount for /// @return uint256 The total staked LINK amount staked in the staking pool function _getTotalPrincipal(IStakingPool stakingPool) private view returns (uint256) { return s_vaultConfig.isOpen ? stakingPool.getTotalPrincipal() : _getFinalTotalPoolPrincipal(stakingPool); } /// @notice Returns the staker's staked LINK amount in a staking pool. This will /// return the staker's latest staked LINK amount if the vault has not been /// closed and the staker's staked LINK amount at the time the vault was /// closed if the vault has already been closed. /// @param staker The staker to query the total staked LINK amount for /// @param stakingPool The staking pool to query the total staked LINK amount for /// @return uint256 The staker's staked LINK amount in the staking pool in juels function _getStakerPrincipal( address staker, IStakingPool stakingPool ) private view returns (uint256) { return s_vaultConfig.isOpen ? stakingPool.getStakerPrincipal(staker) : stakingPool.getStakerPrincipalAt(staker, s_finalVestingCheckpointData.finalBlockNumber); } /// @notice Helper function to get a staker's current multiplier /// @param stakedAt The time the staker last staked at /// @return uint256 The staker's multiplier function _getMultiplier(uint256 stakedAt) private view returns (uint256) { if (stakedAt == 0) return 0; if (!s_vaultConfig.isOpen) return MAX_MULTIPLIER; uint256 multiplierDuration = i_multiplierDuration; if (multiplierDuration == 0) return MAX_MULTIPLIER; return Math.min( FixedPointMathLib.divWadDown(block.timestamp - stakedAt, multiplierDuration), MAX_MULTIPLIER ); } /// @notice Returns the staker's staked at time in a staking pool. This will /// return the staker's latest staked at time if the vault has not been /// closed and the staker's staked at time at the time the vault was /// closed if the vault has already been closed. /// @param staker The staker to query the staked at time for /// @param stakingPool The staking pool to query the staked at time for /// @return uint256 The staker's average staked at time in the staking pool function _getStakerStakedAtTime( address staker, IStakingPool stakingPool ) private view returns (uint256) { return s_vaultConfig.isOpen ? stakingPool.getStakerStakedAtTime(staker) : stakingPool.getStakerStakedAtTimeAt(staker, s_finalVestingCheckpointData.finalBlockNumber); } /// @notice Return the staking pool's total staked LINK amount at the time the vault was /// closed /// @param stakingPool The staking pool to query the total staked LINK amount for /// @return uint256 The pool's total staked LINK amount at the time the vault was /// closed function _getFinalTotalPoolPrincipal(IStakingPool stakingPool) private view returns (uint256) { return address(stakingPool) == address(i_operatorStakingPool) ? s_finalVestingCheckpointData.operatorPoolTotalPrincipal : s_finalVestingCheckpointData.communityPoolTotalPrincipal; } /// @notice Records the final block number and the total staked LINK amounts /// in the operator and community staking pools function _checkpointStakingPools() private { s_finalVestingCheckpointData.operatorPoolTotalPrincipal = i_operatorStakingPool.getTotalPrincipal(); s_finalVestingCheckpointData.communityPoolTotalPrincipal = i_communityStakingPool.getTotalPrincipal(); s_finalVestingCheckpointData.finalBlockNumber = block.number; } /// @notice Stops rewards in a bucket from vesting /// @param bucket The bucket to stop vesting rewards for /// @return uint256 The amount of unvested rewards in juels function _stopVestingBucketRewards(RewardBucket storage bucket) private returns (uint256) { uint256 unvestedRewards = _getUnvestedRewards(bucket); bucket.rewardDurationEndsAt = block.timestamp.toUint80(); return unvestedRewards; } /// @notice Updates the reward buckets /// @param pool The staking pool address /// @param amount The reward amount /// @param emissionRate The target aggregate reward rate (Juels/second) function _updateRewardBuckets(address pool, uint256 amount, uint256 emissionRate) private { // split the reward and aggregate reward rate for the different reward buckets BucketRewardEmissionSplit memory emissionSplitData = _getBucketRewardAndEmissionRateSplit({ pool: pool, amount: amount, emissionRate: emissionRate, isDelegated: s_vaultConfig.delegationRate != 0 }); // If the aggregate reward rate is zero, we don't update the reward bucket // This is because we do not allow a zero aggregate reward rate // A zero aggregate reward rate means no rewards have been added if (emissionSplitData.communityRate != 0) { _updateRewardBucket({ bucket: s_rewardBuckets.communityBase, amount: emissionSplitData.communityReward, emissionRate: emissionSplitData.communityRate }); } if (emissionSplitData.operatorRate != 0) { _updateRewardBucket({ bucket: s_rewardBuckets.operatorBase, amount: emissionSplitData.operatorReward, emissionRate: emissionSplitData.operatorRate }); } if (emissionSplitData.delegatedRate != 0) { _updateRewardBucket({ bucket: s_rewardBuckets.operatorDelegated, amount: emissionSplitData.operatorDelegatedReward, emissionRate: emissionSplitData.delegatedRate }); } } /// @notice Updates the reward bucket /// @param bucket The reward bucket /// @param amount The reward amount /// @param emissionRate The target aggregate reward rate (token/second) function _updateRewardBucket( RewardBucket storage bucket, uint256 amount, uint256 emissionRate ) private { // calculate the remaining rewards uint256 remainingRewards = _getUnvestedRewards(bucket); // if the amount of rewards is less than what becomes available per second, we revert if (amount + remainingRewards < emissionRate) revert RewardDurationTooShort(); _updateRewardDurationEndsAt({ bucket: bucket, rewardAmount: amount + remainingRewards, emissionRate: emissionRate }); bucket.emissionRate = emissionRate.toUint80(); } /// @notice Updates the reward duration end time of the bucket /// @param bucket The reward bucket /// @param rewardAmount The reward amount /// @param emissionRate The aggregate reward rate function _updateRewardDurationEndsAt( RewardBucket storage bucket, uint256 rewardAmount, uint256 emissionRate ) private { if (emissionRate == 0) return; bucket.rewardDurationEndsAt = (block.timestamp + (rewardAmount / emissionRate)).toUint80(); } /// @notice Splits the reward and aggregate reward rates between the different reward buckets /// @dev If the pool is not targeted, the returned reward and aggregate reward rate will be zero /// @param pool The staking pool address (or zero address if the reward is split between all /// pools) /// @param amount The reward amount /// @param emissionRate The aggregate reward rate (juels/second) /// @param isDelegated Whether the reward is delegated or not /// @return BucketRewardEmissionSplit The rewards and aggregate reward rates after /// distributing the reward amount to the buckets function _getBucketRewardAndEmissionRateSplit( address pool, uint256 amount, uint256 emissionRate, bool isDelegated ) private view returns (BucketRewardEmissionSplit memory) { // when splitting reward and rate, a pool's share is 0 if it is not targeted by the pool // address, // otherwise it is the pool's max size // a pool's share is used to split rewards and aggregate reward rates proportionally uint256 communityPoolShare = pool != address(i_operatorStakingPool) ? i_communityStakingPool.getMaxPoolSize() : 0; uint256 operatorPoolShare = pool != address(i_communityStakingPool) ? i_operatorStakingPool.getMaxPoolSize() : 0; uint256 totalPoolShare = communityPoolShare + operatorPoolShare; uint256 operatorReward; uint256 communityReward; uint256 operatorRate; uint256 communityRate; if (pool == address(i_operatorStakingPool)) { operatorReward = amount; operatorRate = emissionRate; } else if (pool == address(i_communityStakingPool)) { communityReward = amount; communityRate = emissionRate; } else { // prevent a possible rounding to zero error by validating inputs _checkForRoundingToZeroRewardAmountSplit({ rewardAmount: amount, operatorPoolShare: operatorPoolShare, totalPoolShare: totalPoolShare }); _checkForRoundingToZeroEmissionRateSplit({ emissionRate: emissionRate, operatorPoolShare: operatorPoolShare, totalPoolShare: totalPoolShare }); operatorReward = amount * operatorPoolShare / totalPoolShare; operatorRate = emissionRate * operatorPoolShare / totalPoolShare; communityReward = amount - operatorReward; communityRate = emissionRate - operatorRate; } uint256 operatorDelegatedReward; uint256 delegatedRate; // if there is no delegation or the community pool is not targeted, the delegated reward and // rate is zero if (isDelegated && communityPoolShare != 0) { // calculate the delegated pool reward and remove from community reward operatorDelegatedReward = communityReward * s_vaultConfig.delegationRate / DELEGATION_BASIS_POINTS_DENOMINATOR; if (communityReward > 0 && operatorDelegatedReward == 0) revert InvalidRewardAmount(); communityReward -= operatorDelegatedReward; // calculate the delegated pool aggregate reward rate and remove from community rate delegatedRate = communityRate * s_vaultConfig.delegationRate / DELEGATION_BASIS_POINTS_DENOMINATOR; if (communityRate > 0 && delegatedRate == 0) revert InvalidEmissionRate(); communityRate -= delegatedRate; } return ( BucketRewardEmissionSplit({ communityReward: communityReward, operatorReward: operatorReward, operatorDelegatedReward: operatorDelegatedReward, communityRate: communityRate, operatorRate: operatorRate, delegatedRate: delegatedRate }) ); } /// @notice Validates the added reward amount after splitting to avoid a rounding error when /// dividing /// @param rewardAmount The reward amount /// @param operatorPoolShare The size of the operator staking pool to take into account /// @param totalPoolShare The total size of the pools to take into account function _checkForRoundingToZeroRewardAmountSplit( uint256 rewardAmount, uint256 operatorPoolShare, uint256 totalPoolShare ) private pure { if ( rewardAmount != 0 && ((operatorPoolShare != 0 && rewardAmount * operatorPoolShare < totalPoolShare)) ) { revert InvalidRewardAmount(); } } /// @notice Validates the aggregate reward rate after splitting to avoid a rounding error when /// dividing /// @param emissionRate The aggregate reward rate /// @param operatorPoolShare The size of the operator staking pool to take into account /// @param totalPoolShare The total size of the pools to take into account function _checkForRoundingToZeroEmissionRateSplit( uint256 emissionRate, uint256 operatorPoolShare, uint256 totalPoolShare ) private pure { if ((operatorPoolShare != 0 && emissionRate * operatorPoolShare < totalPoolShare)) { revert InvalidEmissionRate(); } } /// @notice Private util function to unpack and return reward update timestamps. /// @return uint256 communityRewardUpdateTimestamp /// @return uint256 operatorRewardUpdateTimestamp function _getRewardUpdateTimestamps(uint256 packedRewardUpdateTimestamps) private pure returns (uint256, uint256) { uint256 communityRewardUpdateTimestamp = packedRewardUpdateTimestamps / 1e18; uint256 operatorRewardUpdateTimestamp = packedRewardUpdateTimestamps % 1e18; return (communityRewardUpdateTimestamp, operatorRewardUpdateTimestamp); } /// @notice Private util function to pack and set reward update timestamps. function _setRewardUpdateTimestamps( uint256 communityRewardUpdateTimestamp, uint256 operatorRewardUpdateTimestamp ) private { s_packedRewardUpdateTimestamps = communityRewardUpdateTimestamp * 1e18 + operatorRewardUpdateTimestamp; } /// @notice Private util function for updateRewardPerToken function _updateRewardPerToken() private { (uint256 communityRewardUpdateTimestamp, uint256 operatorRewardUpdateTimestamp) = _getRewardUpdateTimestamps(s_packedRewardUpdateTimestamps); if ( communityRewardUpdateTimestamp == block.timestamp && operatorRewardUpdateTimestamp == block.timestamp ) { // if the pools were previously updated in the same block there is no recalculation of reward return; } ( uint256 communityRewardPerToken, uint256 operatorRewardPerToken, uint256 operatorDelegatedRewardPerToken ) = _calculatePoolsRewardPerToken(); s_rewardBuckets.communityBase.vestedRewardPerToken = communityRewardPerToken.toUint80(); s_rewardBuckets.operatorBase.vestedRewardPerToken = operatorRewardPerToken.toUint80(); s_rewardBuckets.operatorDelegated.vestedRewardPerToken = operatorDelegatedRewardPerToken.toUint80(); _setRewardUpdateTimestamps(block.timestamp, block.timestamp); emit CommunityPoolRewardUpdated(communityRewardPerToken); emit OperatorPoolRewardUpdated(operatorRewardPerToken, operatorDelegatedRewardPerToken); } /// @notice Private util function for updateRewardPerToken /// @param stakerType The staker type to update the reward for. function _updateRewardPerToken(StakerType stakerType) private { (uint256 communityRewardUpdateTimestamp, uint256 operatorRewardUpdateTimestamp) = _getRewardUpdateTimestamps(s_packedRewardUpdateTimestamps); if (stakerType == StakerType.COMMUNITY) { if (communityRewardUpdateTimestamp == block.timestamp) { return; } s_rewardBuckets.communityBase.vestedRewardPerToken = _calculateVestedRewardPerToken( s_rewardBuckets.communityBase, _getTotalPrincipal(i_communityStakingPool), communityRewardUpdateTimestamp ).toUint80(); _setRewardUpdateTimestamps(block.timestamp, operatorRewardUpdateTimestamp); emit CommunityPoolRewardUpdated(s_rewardBuckets.communityBase.vestedRewardPerToken); } else if (stakerType == StakerType.OPERATOR) { if (operatorRewardUpdateTimestamp == block.timestamp) { return; } uint256 operatorTotalPrincipal = _getTotalPrincipal(i_operatorStakingPool); s_rewardBuckets.operatorBase.vestedRewardPerToken = _calculateVestedRewardPerToken( s_rewardBuckets.operatorBase, operatorTotalPrincipal, operatorRewardUpdateTimestamp ).toUint80(); s_rewardBuckets.operatorDelegated.vestedRewardPerToken = _calculateVestedRewardPerToken( s_rewardBuckets.operatorDelegated, operatorTotalPrincipal, operatorRewardUpdateTimestamp ).toUint80(); _setRewardUpdateTimestamps(communityRewardUpdateTimestamp, block.timestamp); emit OperatorPoolRewardUpdated( s_rewardBuckets.operatorBase.vestedRewardPerToken, s_rewardBuckets.operatorDelegated.vestedRewardPerToken ); } } /// @notice Util function for calculating the current reward per token for the pools /// @return uint256 The community reward per token /// @return uint256 The operator reward per token /// @return uint256 The operator delegated reward per token function _calculatePoolsRewardPerToken() private view returns (uint256, uint256, uint256) { uint256 communityTotalPrincipal = _getTotalPrincipal(i_communityStakingPool); uint256 operatorTotalPrincipal = _getTotalPrincipal(i_operatorStakingPool); (uint256 communityRewardUpdateTimestamp, uint256 operatorRewardUpdateTimestamp) = _getRewardUpdateTimestamps(s_packedRewardUpdateTimestamps); return ( _calculateVestedRewardPerToken( s_rewardBuckets.communityBase, communityTotalPrincipal, communityRewardUpdateTimestamp ), _calculateVestedRewardPerToken( s_rewardBuckets.operatorBase, operatorTotalPrincipal, operatorRewardUpdateTimestamp ), _calculateVestedRewardPerToken( s_rewardBuckets.operatorDelegated, operatorTotalPrincipal, operatorRewardUpdateTimestamp ) ); } /// @notice Calculate a bucket’s available rewards earned per token /// @param rewardBucket The reward bucket to calculate the vestedRewardPerToken for /// @param totalPrincipal The total staked LINK amount staked in a pool associated with the reward /// bucket /// @return uint256 The available rewards earned per token function _calculateVestedRewardPerToken( RewardBucket memory rewardBucket, uint256 totalPrincipal, uint256 lastUpdateTimestamp ) private view returns (uint256) { if (totalPrincipal == 0) return rewardBucket.vestedRewardPerToken; uint256 latestRewardEmittedAt = Math.min(rewardBucket.rewardDurationEndsAt, block.timestamp); if (latestRewardEmittedAt <= lastUpdateTimestamp) { return rewardBucket.vestedRewardPerToken; } uint256 elapsedTime = latestRewardEmittedAt - lastUpdateTimestamp; return rewardBucket.vestedRewardPerToken + (elapsedTime * rewardBucket.emissionRate).divWadDown(totalPrincipal); } /// @notice Calculates a stakers earned base reward /// @param stakerReward The staker's reward info /// @param stakerPrincipal The staker's staked LINK amount /// @param baseRewardPerToken The base reward per token of the staking pool /// @return uint256 The earned base reward function _calculateEarnedBaseReward( StakerReward memory stakerReward, uint256 stakerPrincipal, uint256 baseRewardPerToken ) private pure returns (uint256) { uint256 earnedBaseReward = _calculateAccruedReward({ principal: stakerPrincipal, rewardPerToken: stakerReward.baseRewardPerToken, vestedRewardPerToken: baseRewardPerToken }); return earnedBaseReward; } /// @notice Calculates an operator's earned delegated reward /// @param stakerReward The staker's reward info /// @param stakerPrincipal The staker's staked LINK amount /// @param operatorDelegatedRewardPerToken The operator delegated reward per token /// @return uint256 The earned delegated reward function _calculateEarnedDelegatedReward( StakerReward memory stakerReward, uint256 stakerPrincipal, uint256 operatorDelegatedRewardPerToken ) private pure returns (uint256) { uint256 earnedDelegatedReward = _calculateAccruedReward({ principal: stakerPrincipal, rewardPerToken: stakerReward.operatorDelegatedRewardPerToken, vestedRewardPerToken: operatorDelegatedRewardPerToken }); return earnedDelegatedReward; } /// @notice Calculates the newly accrued reward of a staker since the last time the staker's /// reward was updated /// @param principal The staker's staked LINK amount /// @param rewardPerToken The base or delegated reward per token of the staker /// @param vestedRewardPerToken The available reward per token of the staking pool /// @return uint256 The accrued reward amount function _calculateAccruedReward( uint256 principal, uint256 rewardPerToken, uint256 vestedRewardPerToken ) private pure returns (uint256) { return principal.mulWadDown(vestedRewardPerToken - rewardPerToken); } /// @notice Calculates and updates a staker's rewards /// @param staker The staker's address /// @param isOperator True if the staker is an operator, false otherwise /// @param stakerPrincipal The staker's staked LINK amount /// @dev Staker rewards are forfeited when a staker unstakes before they /// have reached their maximum ramp up period multiplier. Additionally an /// operator will also forfeit any unclaimed rewards if they are removed /// before they reach the maximum ramp up period multiplier. /// @return StakerReward The staker's updated reward info function _calculateStakerReward( address staker, bool isOperator, uint256 stakerPrincipal ) private view returns (StakerReward memory) { StakerReward memory stakerReward = s_rewards[staker]; if (stakerReward.stakerType != StakerType.NOT_STAKED) { // do nothing } else { stakerReward.stakerType = isOperator ? StakerType.OPERATOR : StakerType.COMMUNITY; } // Calculate earned base rewards stakerReward.unvestedBaseReward += _calculateEarnedBaseReward({ stakerReward: stakerReward, stakerPrincipal: stakerPrincipal, baseRewardPerToken: isOperator ? s_rewardBuckets.operatorBase.vestedRewardPerToken : s_rewardBuckets.communityBase.vestedRewardPerToken }).toUint112(); // Calculate earned delegated rewards if the staker is an operator if (isOperator) { // Multipliers do not apply to the delegation reward, i.e. always treat them as // multiplied by the max multiplier, which is 1. stakerReward.vestedDelegatedReward += _calculateEarnedDelegatedReward({ stakerReward: stakerReward, stakerPrincipal: stakerPrincipal, operatorDelegatedRewardPerToken: s_rewardBuckets.operatorDelegated.vestedRewardPerToken }).toUint112(); } // Update the staker's earned reward per token _updateStakerRewardPerToken(stakerReward, isOperator); return stakerReward; } /// @notice Helper function for calculating the available reward per token and the reclaimable /// reward /// @dev If the pool the staker is in is empty and we can't calculate the reward per token, we /// allow the staker to reclaim the forfeited reward. /// @param forfeitedReward The amount of forfeited reward /// @param amountOfRecipientTokens The amount of tokens that the forfeited rewards should be /// shared to /// @return uint256 The amount of shared forfeited reward /// @return uint256 The shared forfeited reward per token /// @return uint256 The amount of reclaimable reward function _calculateForfeitedRewardDistribution( uint256 forfeitedReward, uint256 amountOfRecipientTokens ) private pure returns (uint256, uint256, uint256) { if (forfeitedReward == 0) return (0, 0, 0); uint256 vestedReward; uint256 vestedRewardPerToken; uint256 reclaimableReward; if (amountOfRecipientTokens != 0) { vestedReward = forfeitedReward; vestedRewardPerToken = forfeitedReward.divWadDown(amountOfRecipientTokens); } else { reclaimableReward = forfeitedReward; } return (vestedReward, vestedRewardPerToken, reclaimableReward); } /// @notice Updates the staker's base and/or delegated reward per token values /// @dev This function is called when staking, unstaking, claiming rewards, finalizing rewards for /// removed operators, and slashing operators. /// @param stakerReward The staker reward struct /// @param isOperator Whether the staker is an operator or not function _updateStakerRewardPerToken( StakerReward memory stakerReward, bool isOperator ) private view { if (isOperator) { stakerReward.baseRewardPerToken = s_rewardBuckets.operatorBase.vestedRewardPerToken; stakerReward.operatorDelegatedRewardPerToken = s_rewardBuckets.operatorDelegated.vestedRewardPerToken; } else { stakerReward.baseRewardPerToken = s_rewardBuckets.communityBase.vestedRewardPerToken; } } /// @notice Calculates a staker's earned rewards /// @param staker The staker /// @return The staker reward info /// @return The forfeited reward function _getReward( address staker, uint256 stakerPrincipal, bool isOperator ) private view returns (StakerReward memory, uint256) { StakerReward memory stakerReward = s_rewards[staker]; // Calculate latest reward per token for the pools ( uint256 communityRewardPerToken, uint256 operatorRewardPerToken, uint256 operatorDelegatedRewardPerToken ) = _calculatePoolsRewardPerToken(); // Calculate earned base rewards stakerReward.unvestedBaseReward += _calculateEarnedBaseReward({ stakerReward: stakerReward, stakerPrincipal: stakerPrincipal, baseRewardPerToken: isOperator ? operatorRewardPerToken : communityRewardPerToken }).toUint112(); // If operator Calculate earned delegated rewards if (isOperator) { // Multipliers do not apply to the delegation reward, i.e. always treat them as // multiplied by the max multiplier, which is 1. stakerReward.vestedDelegatedReward += _calculateEarnedDelegatedReward({ stakerReward: stakerReward, stakerPrincipal: stakerPrincipal, operatorDelegatedRewardPerToken: operatorDelegatedRewardPerToken }).toUint112(); } uint112 newVestedBaseRewards = _calculateNewVestedBaseRewards({ stakerReward: stakerReward, multiplier: _getMultiplier( _getStakerStakedAtTime( staker, isOperator ? IStakingPool(i_operatorStakingPool) : IStakingPool(i_communityStakingPool) ) ) }); stakerReward.vestedBaseReward += newVestedBaseRewards; uint256 forfeitedRewards = stakerReward.unvestedBaseReward - newVestedBaseRewards; // Forfeit rewards delete stakerReward.unvestedBaseReward; return (stakerReward, forfeitedRewards); } /// @notice Calculates the amount of unvested rewards in a reward bucket /// @param bucket The bucket to calculate unvested rewards for /// @return uint256 The amount of unvested rewards in the bucket function _getUnvestedRewards(RewardBucket memory bucket) private view returns (uint256) { return bucket.rewardDurationEndsAt <= block.timestamp ? 0 : bucket.emissionRate * (bucket.rewardDurationEndsAt - block.timestamp); } /// @notice Returns whether or not an address is currently an operator or /// is a removed operator /// @param staker The staker address /// @return bool True if the staker is either an operator or a removed operator. function _isOperator(address staker) private view returns (bool) { return i_operatorStakingPool.isOperator(staker) || i_operatorStakingPool.isRemoved(staker); } // ========= // Modifiers // ========= /// @dev Reverts if the msg.sender doesn't have the rewarder role. modifier onlyRewarder() { if (!hasRole(REWARDER_ROLE, msg.sender)) { revert AccessForbidden(); } _; } /// @dev Reverts if the msg.sender is not a valid staking pool modifier onlyStakingPool() { if ( msg.sender != address(i_operatorStakingPool) && msg.sender != address(i_communityStakingPool) ) { revert AccessForbidden(); } _; } /// @dev Reverts if the reward vault has been closed modifier whenOpen() { if (!s_vaultConfig.isOpen) revert VaultAlreadyClosed(); _; } // ======================= // TypeAndVersionInterface // ======================= /// @inheritdoc TypeAndVersionInterface function typeAndVersion() external pure virtual override returns (string memory) { return "RewardVault 1.0.0"; } }
// 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 pragma solidity ^0.8.0; abstract contract TypeAndVersionInterface { function typeAndVersion() external pure virtual returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant MAX_UINT256 = 2**256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; interface IRewardVault { /// @notice This enum describes the different staker types enum StakerType { NOT_STAKED, COMMUNITY, OPERATOR } /// @notice This struct is used to store the reward information for a staker. struct StakerReward { /// @notice The staker's accrued multiplier-applied reward that's accounted for and stored. /// This is used for storing delegated rewards and preserving the staker's past rewards between /// unstakes or multiplier resets. /// To get the full claimable reward amount, this value is added to the stored reward * /// multiplier. /// @dev This value is reset when a staker calls claimRewards and rewards /// are transferred to the staker. uint112 vestedBaseReward; /// @notice The staker's accrued delegated reward that's accounted for and stored. /// Delegated rewards are not subject to the ramp up multiplier and are immediately finalized. /// @dev This value is reset when a staker calls claimRewards and rewards /// are transferred to the staker. uint112 vestedDelegatedReward; /// @notice The last updated per-token base reward of the staker. This /// value only increases over time uint112 baseRewardPerToken; /// @notice The last updated per-token delegated reward of the operator uint112 operatorDelegatedRewardPerToken; /// @notice The staker type /// @dev This value is set once the first time a staker stakes. This value is used to enforce /// that a community staker is not added as an operator. StakerType stakerType; /// @notice The amount of base rewards that the staker has claimed between /// the last time they staked/unstaked until they stake, unstake again or /// when an operator is removed. /// @dev This is reset to 0 whenever concludeRewardPeriod is called /// @dev This is set to vestedBaseReward whenever claimReward is called /// @dev Invariant: The sum of unvestedBaseReward and claimedBaseRewardsInPeriod /// is the total amount of base rewards a staker has earned since the last time /// they stake/unstake. uint112 claimedBaseRewardsInPeriod; /// @notice The staker's earned but unvested base rewards. The staker's current multiplier is /// applied to get the vested base reward amount. uint112 unvestedBaseReward; } /// @notice Claims reward earned by a staker. /// @return uint256 The amount of rewards claimed in juels function claimReward() external returns (uint256); /// @notice Updates the staking pools' reward per token and staker’s reward state /// in the reward vault. This is called whenever an operator is slashed as we want /// to update the operator's rewards state without resetting their multiplier. /// @param staker The staker's address. If this is set to zero address, /// staker's reward update will be skipped /// @param stakerPrincipal The staker's current staked LINK amount in juels function updateReward(address staker, uint256 stakerPrincipal) external; /// @notice Concludes the staker's current reward period (defined by a multiplier reset). /// This will apply the staker's current ramp up multiplier to their /// earned rewards and store the amount of rewards they have earned before /// their multiplier is reset. /// @dev This is called whenever 1) A staker stakes 2) A staker unstakes /// 3) An operator is removed as we want to update the staker's /// rewards AND reset their multiplier. /// @dev Staker rewards are not forfeited when they stake before they have /// reached their maximum ramp up period multiplier. Instead these /// rewards are stored as already earned rewards and will be subject to the /// multiplier the next time the contract calculates the staker's claimable /// rewards. /// @dev Staker rewards are forfeited when a staker unstakes before they /// have reached their maximum ramp up period multiplier. Additionally an /// operator will also forfeit any unclaimable rewards if they are removed /// before they reach the maximum ramp up period multiplier. The amount of /// rewards forfeited is proportional to the amount unstaked relative to /// the staker's total staked LINK amount when unstaking. A removed operator forfeits /// 100% of their unclaimable rewards. /// @param staker The staker addres /// @param oldPrincipal The staker's staked LINK amount before finalizing /// @param stakedAt The last time the staker staked at /// @param unstakedAmount The amount that the staker has unstaked in juels /// @param shouldForfeit True if rewards should be forfeited function concludeRewardPeriod( address staker, uint256 oldPrincipal, uint256 stakedAt, uint256 unstakedAmount, bool shouldForfeit ) external; /// @notice Closes the reward vault, disabling adding rewards and staking function close() external; /// @notice Returns a boolean that is true if the reward vault is open /// @return True if open, false otherwise function isOpen() external view returns (bool); /// @notice Returns the rewards that the staker would get if they withdraw now /// Rewards calculation is based on the staker's multiplier /// @param staker The staker's address /// @return The reward amount function getReward(address staker) external view returns (uint256); /// @notice Returns the stored reward info of the staker /// @param staker The staker address /// @return The staker's stored reward info function getStoredReward(address staker) external view returns (StakerReward memory); /// @notice Returns whether or not the vault is paused /// @return bool True if the vault is paused function isPaused() external view returns (bool); /// @notice Returns whether or not the reward duration for the pool has ended /// @param stakingPool The address of the staking pool rewards are being shared to /// @return bool True if the reward duration has ended function hasRewardDurationEnded(address stakingPool) external view returns (bool); /// @notice Returns whether or not the reward vault has had rewards added to it /// @return bool True if the reward vault has had rewards added to it function hasRewardAdded() external view returns (bool); /// @notice Returns the staking pools that are earning rewards from /// the reward vault /// @return address[] The staking pools that are earning rewards from the /// reward vault function getStakingPools() external view returns (address[] memory); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {IRewardVault} from "./IRewardVault.sol"; import {Checkpoints} from "@openzeppelin/contracts/utils/Checkpoints.sol"; interface IStakingPool { /// @notice This error is thrown when a caller tries to execute a transaction /// that they do not have permissions for error AccessForbidden(); /// @notice This event is emitted when the migration proxy address has been set /// @param oldMigrationProxy The old migration proxy contract address /// @param newMigrationProxy The new migration proxy contract address event MigrationProxySet(address indexed oldMigrationProxy, address indexed newMigrationProxy); /// @notice This event is emitted when the staking pool's maximum size is /// increased /// @param maxPoolSize the new maximum pool size event PoolSizeIncreased(uint256 maxPoolSize); /// @notice This event is emitted when the maximum stake amount // for the stakers in the pool is increased /// @param maxPrincipalPerStaker the new maximum stake amount event MaxPrincipalAmountIncreased(uint256 maxPrincipalPerStaker); /// @notice This event is emitted when a staker adds stake to the pool. /// @param staker Staker address /// @param amount Amount of stake added /// @param newStake New LINK amount staked /// @param newTotalPrincipal Total amount of juels staked in the pool event Staked(address indexed staker, uint256 amount, uint256 newStake, uint256 newTotalPrincipal); /// @notice This event is emitted when a staker removes stake from the pool. /// @param staker Staker address /// @param amount Amount of stake removed /// @param newStake New LINK amount staked /// @param newTotalPrincipal Total amount of staked juels remaining in the pool event Unstaked( address indexed staker, uint256 amount, uint256 newStake, uint256 newTotalPrincipal ); /// @notice This error is thrown whenever a zero-address is supplied when /// a non-zero address is required error InvalidZeroAddress(); /// @notice This error is thrown whenever the sender is not the LINK token error SenderNotLinkToken(); /// @notice This error is thrown whenever the migration proxy address has not been set error MigrationProxyNotSet(); /// @notice This error is thrown whenever the reward vault address has not been set error RewardVaultNotSet(); /// @notice This error is thrown when invalid data is passed to the onTokenTransfer function error InvalidData(); /// @notice This error is thrown when the staker tries to stake less than the min amount error InsufficientStakeAmount(); /// @notice This error is thrown when the staker tries to stake more than the max amount error ExceedsMaxStakeAmount(); /// @notice This error is thrown when the staker tries to stake more than the max pool size error ExceedsMaxPoolSize(); /// @notice This error is raised when stakers attempt to exit the pool /// @param staker address of the staker error StakeNotFound(address staker); /// @notice This error is thrown when the staker tries to unstake a zero amount error UnstakeZeroAmount(); /// @notice This error is thrown when the staker tries to unstake more than the /// staked LINK amount error UnstakeExceedsPrincipal(); /// @notice This error is thrown when the staker tries to unstake an amount that leaves their /// staked LINK amount below the minimum amount error UnstakePrincipalBelowMinAmount(); /// @notice This struct defines the state of a staker struct Staker { /// @notice The combined staked LINK amount and staked at time history /// @dev Both the staker staked LINK amount and staked at timestamp are stored in uint112 to /// save space /// @dev The max value of uint112 is greater than the total supply of LINK /// @dev The max value of uint112 can represent a timestamp in the year 3615, long after the /// staking program has ended /// @dev The combination is performed as such: /// uint224 history = (uint224(uint112(principal)) << 112) | /// uint224(uint112(stakedAtTime)) Checkpoints.History history; /// @notice The staker's unbonding period end time uint128 unbondingPeriodEndsAt; /// @notice The staker's claim period end time uint128 claimPeriodEndsAt; } /// @notice Unstakes amount LINK tokens from the staker’s staked LINK amount /// @param amount The amount of LINK tokens to unstake function unstake(uint256 amount) external; /// @notice Returns the total amount staked in the pool /// @return The total amount staked in pool function getTotalPrincipal() external view returns (uint256); /// @notice Returns the staker's staked LINK amount /// @param staker The address of the staker to query for /// @return uint256 The staker's staked LINK amount function getStakerPrincipal(address staker) external view returns (uint256); /// @notice Returns the staker's staked LINK amount /// @param staker The address of the staker to query for /// @param blockNumber The block number to fetch the staker's balance for. Pass 0 /// to return the staker's latest staked LINK amount /// @return uint256 The staker's staked LINK amount function getStakerPrincipalAt( address staker, uint256 blockNumber ) external view returns (uint256); /// @notice Returns the staker's average staked at time /// @param staker The address of the staker to query for /// @return uint256 The staker's average staked at time function getStakerStakedAtTime(address staker) external view returns (uint256); /// @notice Returns the staker's last staked at time for a block number ID /// @param staker The address of the staker to query for /// @param blockNumber The block number to query for /// @return uint256 The staker's staked at time for the block number ID function getStakerStakedAtTimeAt( address staker, uint256 blockNumber ) external view returns (uint256); /// @notice Returns the current reward vault address /// @return The reward vault function getRewardVault() external view returns (IRewardVault); /// @notice Returns the address of the LINK token contract /// @return The LINK token contract's address that is used by the pool function getChainlinkToken() external view returns (address); /// @notice Returns the migration proxy contract address /// @return The migration proxy contract address function getMigrationProxy() external view returns (address); /// @notice Returns a boolean that is true if the pool is open /// @return True if the pool is open, false otherwise function isOpen() external view returns (bool); /// @notice Returns a boolean that is true if the pool is active, /// i.e. is open and there are remaining rewards to vest in the pool. /// @return True if the pool is active, false otherwise function isActive() external view returns (bool); /// @notice Returns the minimum and maximum amounts a staker can stake in the /// pool /// @return uint256 minimum amount that can be staked by a staker /// @return uint256 maximum amount that can be staked by a staker function getStakerLimits() external view returns (uint256, uint256); /// @notice uint256 Returns the maximum amount that can be staked in the pool /// @return uint256 current maximum staking pool size function getMaxPoolSize() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {AccessControlDefaultAdminRules} from "@openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol"; import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; import {IPausable} from "./interfaces/IPausable.sol"; /// @notice Base contract that adds pausing and access control functionality. abstract contract PausableWithAccessControl is IPausable, Pausable, AccessControlDefaultAdminRules { /// @notice This is the ID for the pauser role, which is given to the addresses that can pause and /// unpause the contract. /// @dev Hash: 65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); constructor( uint48 adminRoleTransferDelay, address defaultAdmin ) AccessControlDefaultAdminRules(adminRoleTransferDelay, defaultAdmin) {} /// @inheritdoc IPausable function emergencyPause() external onlyRole(PAUSER_ROLE) { _pause(); } /// @inheritdoc IPausable function emergencyUnpause() external onlyRole(PAUSER_ROLE) { _unpause(); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {TypeAndVersionInterface} from "@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol"; import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import {IMerkleAccessController} from "../interfaces/IMerkleAccessController.sol"; import {OperatorStakingPool} from "./OperatorStakingPool.sol"; import {StakingPoolBase} from "./StakingPoolBase.sol"; /// @notice This contract manages the staking of LINK tokens for the community stakers. /// @dev This contract inherits the StakingPoolBase contract and interacts with the MigrationProxy, /// OperatorStakingPool, and RewardVault contracts. /// @dev invariant Operators cannot stake in the community staking pool. contract CommunityStakingPool is StakingPoolBase, IMerkleAccessController, TypeAndVersionInterface { /// @notice This error is thrown when the pool is opened with an empty /// merkle root error MerkleRootNotSet(); /// @notice This event is emitted when the operator staking pool is changed /// @param oldOperatorStakingPool The old operator staking pool /// @param newOperatorStakingPool The new operator staking pool event OperatorStakingPoolChanged( address indexed oldOperatorStakingPool, address indexed newOperatorStakingPool ); /// @notice This struct defines the params required by the Staking contract's /// constructor. struct ConstructorParams { /// @notice The base staking pool constructor parameters ConstructorParamsBase baseParams; /// @notice The operator staking pool contract OperatorStakingPool operatorStakingPool; } /// @notice The operator staking pool contract OperatorStakingPool private s_operatorStakingPool; /// @notice The merkle root of the merkle tree generated from the list /// of staker addresses with early access. bytes32 private s_merkleRoot; constructor(ConstructorParams memory params) StakingPoolBase(params.baseParams) { if (address(params.operatorStakingPool) == address(0)) { revert InvalidZeroAddress(); } s_operatorStakingPool = params.operatorStakingPool; } // ======================= // IMerkleAccessController // ======================= /// @inheritdoc IMerkleAccessController function hasAccess(address staker, bytes32[] calldata proof) external view returns (bool) { return _hasAccess(staker, proof); } /// @inheritdoc IMerkleAccessController /// @dev precondition The caller must have the initiator admin role. /// @dev precondition Cannot be called after the pool is closed. function setMerkleRoot(bytes32 newMerkleRoot) external onlyRole(INITIATOR_ROLE) whenBeforeClosing { bytes32 oldMerkleRoot = s_merkleRoot; if (oldMerkleRoot == newMerkleRoot) return; s_merkleRoot = newMerkleRoot; emit MerkleRootChanged(oldMerkleRoot, newMerkleRoot); } /// @inheritdoc IMerkleAccessController function getMerkleRoot() external view returns (bytes32) { return s_merkleRoot; } /// @notice This function sets the operator staking pool /// @param newOperatorStakingPool The new operator staking pool /// @dev precondition The caller must have the default admin role. function setOperatorStakingPool(OperatorStakingPool newOperatorStakingPool) external onlyRole(DEFAULT_ADMIN_ROLE) { if (address(newOperatorStakingPool) == address(0)) revert InvalidZeroAddress(); address oldOperatorStakingPool = address(s_operatorStakingPool); if (oldOperatorStakingPool == address(newOperatorStakingPool)) return; s_operatorStakingPool = newOperatorStakingPool; emit OperatorStakingPoolChanged(oldOperatorStakingPool, address(newOperatorStakingPool)); } // ======================= // TypeAndVersionInterface // ======================= /// @inheritdoc TypeAndVersionInterface function typeAndVersion() external pure virtual override returns (string memory) { return "CommunityStakingPool 1.0.0"; } // =============== // StakingPoolBase // =============== /// @inheritdoc StakingPoolBase function _validateOnTokenTransfer( address sender, address staker, bytes calldata data ) internal view override(StakingPoolBase) { // check if staker has access // if the sender is the migration proxy, the staker is allowed to stake // if currently in public phase (merkle root set to empty bytes) data is ignored // if in the access limited phase data is the merkle proof // if in migrations only phase, the merkle root is set to double hash of the migration proxy // address. This is essentially only used as a placeholder to differentiate between the open // phase (empty merkle root) and access limited phase (merkle root generated from allowlist) if ( sender != address(s_migrationProxy) && s_merkleRoot != bytes32(0) && !_hasAccess(staker, abi.decode(data, (bytes32[]))) ) { revert AccessForbidden(); } // check if the sender is an operator if (s_operatorStakingPool.isOperator(staker) || s_operatorStakingPool.isRemoved(staker)) { revert AccessForbidden(); } } /// @inheritdoc StakingPoolBase function _validateBeforeOpen() internal view override(StakingPoolBase) { if (s_merkleRoot == bytes32(0)) { revert MerkleRootNotSet(); } } /// @notice Util function that validates if a community staker has access to an /// access limited community staking pool /// @param staker The community staker's address /// @param proof Merkle proof for the community staker's allowlist /// @return bool True if the community staker has access to the access limited /// community staking pool function _hasAccess(address staker, bytes32[] memory proof) private view returns (bool) { if (s_merkleRoot == bytes32(0)) return true; return MerkleProof.verify({ proof: proof, root: s_merkleRoot, leaf: keccak256(bytes.concat(keccak256(abi.encode(staker)))) }); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {TypeAndVersionInterface} from "@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol"; import {AccessControlDefaultAdminRules} from "@openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol"; import {Checkpoints} from "@openzeppelin/contracts/utils/Checkpoints.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {ISlashable} from "../interfaces/ISlashable.sol"; import {IRewardVault} from "../interfaces/IRewardVault.sol"; import {StakingPoolBase} from "./StakingPoolBase.sol"; /// @notice This contract manages the staking of LINK tokens for the operator stakers. /// @dev This contract inherits the StakingPoolBase contract and interacts with the MigrationProxy, /// PriceFeedAlertsController, CommunityStakingPool, and RewardVault contracts. /// @dev invariant Only addresses added as operators by the contract manager can stake in this pool. /// @dev invariant contract's LINK token balance should be greater than or equal to the sum of /// totalPrincipal and s_alerterRewardFunds. contract OperatorStakingPool is ISlashable, StakingPoolBase, TypeAndVersionInterface { using Checkpoints for Checkpoints.History; using EnumerableSet for EnumerableSet.AddressSet; /// @notice This error is raised when adding the zero address as an operator error InvalidOperator(); /// @notice Error code for when the operator list is invalid error InvalidOperatorList(); /// @notice Error code for when the staker is not an operator error StakerNotOperator(); /// @notice This error is raised when an address is duplicated in the supplied list of operators. /// This can happen in addOperators and setFeedOperators functions. /// @param operator address of the operator error OperatorAlreadyExists(address operator); /// @notice This error is raised when removing an operator that doesn't exist. /// @param operator Address of the operator error OperatorDoesNotExist(address operator); /// @notice This error is raised when an operator to add has been removed previously. /// @param operator Address of the operator error OperatorHasBeenRemoved(address operator); /// @notice This error is raised when an operator to add is already a community staker. error OperatorCannotBeCommunityStaker(address operator); /// @notice This error is thrown whenever the max pool size is less than the /// reserved space for operators /// @param maxPoolSize The maximum pool size of the operator staking pool /// @param maxPrincipalPerStaker The maximum amount an operator can stake in the /// pool /// @param numOperators The number of operators in the pool error InsufficientPoolSpace( uint256 maxPoolSize, uint256 maxPrincipalPerStaker, uint256 numOperators ); /// @notice This error is raised when attempting to open the staking pool with less /// than the minimum required node operators /// @param numOperators The current number of operators in the staking pool /// @param minInitialOperatorCount The minimum required number of operators /// in the staking pool before it can be opened error InadequateInitialOperatorCount(uint256 numOperators, uint256 minInitialOperatorCount); /// @notice This error is thrown when the contract manager tries to add a zero amount /// to the alerter reward funds error InvalidAlerterRewardFundAmount(); /// @notice This error is thrown whenever the contract manager tries to withdraw /// more than the remaining balance in the alerter reward funds /// @param amountToWithdraw The amount that the contract manager tried to withdraw /// @param remainingBalance The remaining balance of the alerter reward funds error InsufficientAlerterRewardFunds(uint256 amountToWithdraw, uint256 remainingBalance); /// @notice This event is emitted when an operator is removed /// @param operator Address of the operator /// @param principal Operator's staked LINK amount /// @param newTotalPrincipal Total amount of staked juels remaining in the pool event OperatorRemoved(address indexed operator, uint256 principal, uint256 newTotalPrincipal); /// @notice This event is emitted when an operator is added /// @param operator Address of the operator event OperatorAdded(address indexed operator); /// @notice This event is emitted whenever the alerter reward funds is funded /// @param amountFunded The amount added to the alerter reward funds /// @param totalBalance The current balance of the alerter reward funds event AlerterRewardDeposited(uint256 amountFunded, uint256 totalBalance); /// @notice This event is emitted whenever the contract manager withdraws from the /// alerter reward funds /// @param amountWithdrawn The amount withdrawn from the alerter reward funds /// @param remainingBalance The remaining balance of the alerter reward funds event AlerterRewardWithdrawn(uint256 amountWithdrawn, uint256 remainingBalance); /// @notice This event is emitted whenever the alerter is paid the full /// alerter reward amount /// @param alerter The address of the alerter /// @param alerterRewardActual The amount of rewards sent to the alerter in juels. /// This can be lower than the expected value, if the reward fund is low or we aren't able to /// slash enough. /// @param alerterRewardExpected The amount of expected rewards for the alerter /// in juels event AlertingRewardPaid( address indexed alerter, uint256 alerterRewardActual, uint256 alerterRewardExpected ); /// @notice This event is emitted when the slasher config is set /// @param slasher The address of the slasher /// @param refillRate The refill rate of the slasher /// @param slashCapacity The slash capacity of the slasher event SlasherConfigSet(address indexed slasher, uint256 refillRate, uint256 slashCapacity); /// @notice This event is emitted when an operator is slashed /// @param operator The address of the operator /// @param slashedAmount The amount slashed from the operator's staked LINK /// amount /// @param updatedStakerPrincipal The operator's updated staked LINK amount /// @param newTotalPrincipal Total amount of staked juels remaining in the pool event Slashed( address indexed operator, uint256 slashedAmount, uint256 updatedStakerPrincipal, uint256 newTotalPrincipal ); /// @notice This struct defines the params required by the Staking contract's /// constructor. struct ConstructorParams { /// @notice The base staking pool constructor parameters ConstructorParamsBase baseParams; /// @notice The minimum number of node operators required to open the /// staking pool. uint256 minInitialOperatorCount; } /// @notice This struct defines the operator-specific states. struct Operator { /// @notice The operator's staked LINK amount when they get removed. uint256 removedPrincipal; /// @notice Flag that signals whether the operator is an operator. bool isOperator; /// @notice Flag that signals whether the operator has been removed. bool isRemoved; } /// @notice This is the ID for the alert rewarder role, which is given to the /// addresses that will deposit and withdraw the alerter reward. /// @dev Hash: 8d2cf17e37ecc80f26d65bcf3868b78960ab38b0762747f6c5e311e75068a88b bytes32 public constant ALERT_REWARDER_ROLE = keccak256("ALERT_REWARDER_ROLE"); /// @notice This is the ID for the operator manager role, which is given to the address that will /// add and remove operators /// @dev Hash: 001fdceeaab2d33566b504ecfe97e6dc3cf82cc816e696d9fe5cce35954bed17 bytes32 public constant OPERATOR_MANAGER_ROLE = keccak256("OPERATOR_MANAGER_ROLE"); /// @notice This is the ID for the slasher role, which will be given to the /// AlertsController contract. /// @dev Hash: 12b42e8a160f6064dc959c6f251e3af0750ad213dbecf573b4710d67d6c28e39 bytes32 public constant SLASHER_ROLE = keccak256("SLASHER_ROLE"); /// @notice Mapping of addresses to the Operator struct. mapping(address operator => Operator) private s_operators; /// @notice Mapping of the slashers to slasher config and state. mapping(address slasher => Slasher) private s_slashers; /// @notice The set of operators that are currently on-feed (slashable). EnumerableSet.AddressSet private s_operatorSet; /// @notice The number of node operators that have been set in the pool uint256 private s_numOperators; /// @notice Tracks the balance of the alerter reward funds. This bucket holds all /// slashed funds and also funds alerter rewards. uint256 private s_alerterRewardFunds; /// @notice The minimum number of node operators required to open the /// staking pool. uint256 private immutable i_minInitialOperatorCount; constructor(ConstructorParams memory params) StakingPoolBase(params.baseParams) { i_minInitialOperatorCount = params.minInitialOperatorCount; } /// @notice Adds LINK to the alerter reward funds /// @param amount The amount of LINK to add to the alerter reward funds /// @dev precondition The caller must have the alert rewarder role. /// @dev precondition The caller must have at least `amount` LINK tokens. /// @dev precondition The caller must have approved this contract for the transfer of at least /// `amount` LINK tokens. function depositAlerterReward(uint256 amount) external onlyRole(ALERT_REWARDER_ROLE) whenBeforeClosing { if (amount == 0) revert InvalidAlerterRewardFundAmount(); uint256 alerterRewardFunds = s_alerterRewardFunds; alerterRewardFunds += amount; s_alerterRewardFunds = alerterRewardFunds; // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transferFrom({from: msg.sender, to: address(this), value: amount}); emit AlerterRewardDeposited(amount, alerterRewardFunds); } /// @notice Withdraws LINK from the alerter reward funds /// @param amount The amount of LINK withdrawn from the alerter reward funds /// @dev precondition The caller must have the alert rewarder role. /// @dev precondition This contract must have at least `amount` LINK tokens as the alerter reward /// funds. /// @dev precondition This contract must be closed (before opening or after closing). function withdrawAlerterReward(uint256 amount) external onlyRole(ALERT_REWARDER_ROLE) { if (amount == 0) revert InvalidAlerterRewardFundAmount(); if (s_isOpen) revert PoolNotClosed(); uint256 alerterRewardFunds = s_alerterRewardFunds; if (amount > alerterRewardFunds) { revert InsufficientAlerterRewardFunds(amount, alerterRewardFunds); } alerterRewardFunds -= amount; s_alerterRewardFunds = alerterRewardFunds; // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transfer(msg.sender, amount); emit AlerterRewardWithdrawn(amount, alerterRewardFunds); } /// @notice Returns the balance of the pool's alerter reward funds /// @return uint256 The balance of the pool's alerter reward funds function getAlerterRewardFunds() external view returns (uint256) { return s_alerterRewardFunds; } // =============== // StakingPoolBase // =============== /// @inheritdoc StakingPoolBase /// @dev The access control is done in StakingPoolBase. function setPoolConfig( uint256 maxPoolSize, uint256 maxPrincipalPerStaker ) external override(StakingPoolBase) validatePoolSpace(maxPoolSize, maxPrincipalPerStaker, s_numOperators) whenOpen onlyRole(DEFAULT_ADMIN_ROLE) { _setPoolConfig(maxPoolSize, maxPrincipalPerStaker); } /// @inheritdoc StakingPoolBase /// @dev Removed operators need to go through the unbonding period before they can withdraw. This /// function will check if the operator has removed principal they can unstake. function unbond() external override { Staker storage staker = s_stakers[msg.sender]; uint224 history = staker.history.latest(); uint112 stakerPrincipal = uint112(history >> 112); if (stakerPrincipal == 0 && s_operators[msg.sender].removedPrincipal == 0) { revert StakeNotFound(msg.sender); } _unbond(staker); } /// @notice Registers operators from a list of unique, sorted addresses /// Addresses must be provided in sorted order so that /// address(0xNext) > address(0xPrev) /// @dev Previously removed operators cannot be readded to the pool. /// @dev precondition The caller must have the operator manager role. /// @dev precondition Cannot be called after the pool is closed. /// @param operators The sorted list of operator addresses function addOperators(address[] calldata operators) external whenBeforeClosing validateRewardVaultSet validatePoolSpace( s_pool.configs.maxPoolSize, s_pool.configs.maxPrincipalPerStaker, s_numOperators + operators.length ) onlyRole(OPERATOR_MANAGER_ROLE) { for (uint256 i; i < operators.length; ++i) { address operatorAddress = operators[i]; if (operatorAddress == address(0)) revert InvalidOperator(); IRewardVault.StakerReward memory stakerReward = s_rewardVault.getStoredReward(operatorAddress); if (stakerReward.stakerType == IRewardVault.StakerType.COMMUNITY) { revert OperatorCannotBeCommunityStaker(operatorAddress); } // verify input list is sorted and addresses are unique if (i < operators.length - 1 && operatorAddress >= operators[i + 1]) { revert InvalidOperatorList(); } Operator storage operator = s_operators[operatorAddress]; if (operator.isOperator) revert OperatorAlreadyExists(operatorAddress); if (operator.isRemoved) revert OperatorHasBeenRemoved(operatorAddress); operator.isOperator = true; s_operatorSet.add(operatorAddress); emit OperatorAdded(operatorAddress); } unchecked { s_numOperators += operators.length; } } /// @notice Removes one or more operators from a list of operators. /// @dev Should only be callable by the owner when the pool is open. /// When an operator is removed, we store their staked LINK amount in a separate mapping to /// stop it from accruing rewards. They can withdraw their removedPrincipal and exit the system /// after going through the unbonding period. /// Removed operators are no longer slashable. /// @param operators A list of operator addresses to remove /// @dev precondition The caller must have the operator manager role. /// @dev precondition Cannot be called after the pool is closed. /// @dev precondition The operators must be currently added operators. function removeOperators(address[] calldata operators) external onlyRole(OPERATOR_MANAGER_ROLE) whenBeforeClosing { Operator storage operator; Staker storage staker; uint256 totalPrincipal = s_pool.state.totalPrincipal; for (uint256 i; i < operators.length; ++i) { address operatorAddress = operators[i]; operator = s_operators[operatorAddress]; if (!operator.isOperator) revert OperatorDoesNotExist(operatorAddress); staker = s_stakers[operatorAddress]; uint224 history = staker.history.latest(); uint256 principal = uint256(history >> 112); uint256 stakedAtTime = uint112(history); s_rewardVault.concludeRewardPeriod({ staker: operatorAddress, oldPrincipal: principal, unstakedAmount: principal, shouldForfeit: true, stakedAt: stakedAtTime }); totalPrincipal -= principal; s_pool.state.totalPrincipal = totalPrincipal; delete operator.isOperator; s_operatorSet.remove(operatorAddress); operator.isRemoved = true; // Reset the staker's stakedAtTime to 0 so their multiplier resets to 0. _updateStakerHistory({staker: staker, latestPrincipal: 0, latestStakedAtTime: 0}); // Move the operator's staked LINK amount to removedPrincipal so that // the operator stops earning rewards operator.removedPrincipal = principal; _resetUnbondingPeriod(staker, operatorAddress); emit OperatorRemoved(operatorAddress, principal, totalPrincipal); } s_numOperators -= operators.length; } /// @notice Getter function to check if an address is registered as an operator /// @param staker The address of the staker /// @return bool True if the staker is an operator function isOperator(address staker) external view returns (bool) { return s_operators[staker].isOperator; } /// @notice Getter function to check if an address is a removed operator /// @param staker The address of the staker /// @return bool True if the operator has been removed function isRemoved(address staker) external view returns (bool) { return s_operators[staker].isRemoved; } /// @notice Getter function for a removed operator's total staked LINK amount /// @param staker The address of the staker /// @return uint256 The removed operator's staked LINK amount that hasn't been withdrawn function getRemovedPrincipal(address staker) external view returns (uint256) { return s_operators[staker].removedPrincipal; } /// @notice Called by removed operators to withdraw their removed stake /// @dev precondition The caller must be in the claim period or the pool must be closed or paused. /// @dev precondition The caller must be a removed operator with some removed /// staked LINK amount. function unstakeRemovedPrincipal() external { if (!_canUnstake(s_stakers[msg.sender])) { revert StakerNotInClaimPeriod(msg.sender); } uint256 withdrawableAmount = s_operators[msg.sender].removedPrincipal; if (withdrawableAmount == 0) { revert UnstakeExceedsPrincipal(); } delete s_operators[msg.sender].removedPrincipal; // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transfer(msg.sender, withdrawableAmount); // Since operator has been removed their total amount staked will be 0 emit Unstaked(msg.sender, withdrawableAmount, 0, s_pool.state.totalPrincipal); } /// @notice Returns the number of operators configured in the pool. /// @return uint256 The number of operators configured in the pool function getNumOperators() external view returns (uint256) { return s_numOperators; } /// @notice Returns the list of operators configured in the pool. /// @return address[] The list of operators configured in the pool function getOperators() external view returns (address[] memory) { return s_operatorSet.values(); } // ======================= // ISlashable // ======================= /// @inheritdoc ISlashable /// @dev precondition The caller must have the default admin role. /// @dev precondition Cannot be called after the pool is closed. function addSlasher( address slasher, SlasherConfig calldata config ) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing { _grantRole(SLASHER_ROLE, slasher); _setSlasherConfig(slasher, config); } /// @inheritdoc ISlashable /// @dev precondition The caller must have the default admin role. /// @dev precondition Cannot be called after the pool is closed. function removeSlasher(address slasher) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing { if (!hasRole(SLASHER_ROLE, slasher)) { revert InvalidSlasher(); } delete s_slashers[slasher]; _revokeRole(SLASHER_ROLE, slasher); emit SlasherConfigSet(slasher, 0, 0); } /// @inheritdoc ISlashable /// @dev precondition The caller must have the default admin role. /// @dev precondition Cannot be called after the pool is closed. function setSlasherConfig( address slasher, SlasherConfig calldata config ) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing { if (!hasRole(SLASHER_ROLE, slasher)) { revert InvalidSlasher(); } _setSlasherConfig(slasher, config); } /// @inheritdoc ISlashable function getSlasherConfig(address slasher) external view returns (SlasherConfig memory) { return s_slashers[slasher].config; } /// @inheritdoc ISlashable function getSlashCapacity(address slasher) external view returns (uint256) { SlasherConfig memory slasherConfig = s_slashers[slasher].config; return _getRemainingSlashCapacity(slasherConfig, slasher); } /// @inheritdoc ISlashable /// @dev In the current implementation, on-feed operators can raise alerts to rescue a portion of /// their slashed staked LINK amount. All operators can raise alerts in the priority period. Note /// that this may change in the future as we add alerting for additional services. /// @dev We will operationally make sure to remove an operator from the slashable (on-feed) /// operators list in alerts controllers if they are removed from the operators list in this /// contract, so there won't be a case where we slash a removed operator. /// @dev precondition The caller must have the slasher role. /// @dev precondition This contract must be active (open and stakers are earning rewards). /// @dev precondition The slasher must have enough capacity to slash. function slashAndReward( address[] calldata stakers, address alerter, uint256 principalAmount, uint256 alerterRewardAmount ) external onlySlasher whenActive whenNotPaused { SlasherConfig storage slasherConfig = s_slashers[msg.sender].config; uint256 combinedSlashAmount = stakers.length * principalAmount; uint256 remainingSlashCapacity = _getRemainingSlashCapacity(slasherConfig, msg.sender); // check if the total slashed amount exceeds the slasher's capacity if (combinedSlashAmount > remainingSlashCapacity) { /// @dev If a slashing occurs with an amount to be slashed that is higher than the remaining /// slashing capacity, only an amount equal to the remaining capacity is slashed. principalAmount = remainingSlashCapacity / stakers.length; } uint256 totalSlashedAmount = _slashOperators(stakers, principalAmount); s_slashers[msg.sender].state.remainingSlashCapacityAmount = remainingSlashCapacity - totalSlashedAmount; s_slashers[msg.sender].state.lastSlashTimestamp = block.timestamp; _payAlerter({ alerter: alerter, totalSlashedAmount: totalSlashedAmount, alerterRewardAmount: alerterRewardAmount }); } // ======================= // TypeAndVersionInterface // ======================= /// @inheritdoc TypeAndVersionInterface function typeAndVersion() external pure virtual override returns (string memory) { return "OperatorStakingPool 1.0.0"; } // ============================== // AccessControlDefaultAdminRules // ============================== /// @inheritdoc AccessControlDefaultAdminRules /// @notice Grants `role` to `account`. Reverts if the contract manager tries to grant the default /// admin or slasher role. /// @dev The default admin role must be granted through `beginDefaultAdminTransfer` and /// `acceptDefaultAdminTransfer`. /// @dev The slasher role must be granted through `addSlasher`. /// @param role The role to grant /// @param account The address to grant the role to function grantRole( bytes32 role, address account ) public virtual override(AccessControlDefaultAdminRules) { if (role == SLASHER_ROLE) revert InvalidRole(); super.grantRole(role, account); } /// @inheritdoc AccessControlDefaultAdminRules /// @notice Grants `role` to `account`. Reverts if the contract manager tries to grant the default /// admin or slasher role. /// @dev The default admin role must be revoked through `beginDefaultAdminTransfer` and /// `acceptDefaultAdminTransfer` to another address. /// @dev The slasher role must be revoked through `removeSlasher`. /// @param role The role to revoke /// @param account The address to revoke the role from function revokeRole( bytes32 role, address account ) public virtual override(AccessControlDefaultAdminRules) { if (role == SLASHER_ROLE) revert InvalidRole(); super.revokeRole(role, account); } // =============== // StakingPoolBase // =============== /// @inheritdoc StakingPoolBase function _validateOnTokenTransfer( address, address staker, bytes calldata ) internal view override(StakingPoolBase) { // check if staker is an operator if (!s_operators[staker].isOperator) revert StakerNotOperator(); } /// @inheritdoc StakingPoolBase function _validateBeforeOpen() internal view override(StakingPoolBase) { if (s_numOperators < i_minInitialOperatorCount) { revert InadequateInitialOperatorCount(s_numOperators, i_minInitialOperatorCount); } } /// @notice Helper function to set the slasher config /// @param slasher The slasher /// @param config The slasher config function _setSlasherConfig(address slasher, SlasherConfig calldata config) private { if (config.slashCapacity == 0 || config.refillRate == 0) { revert ISlashable.InvalidSlasherConfig(); } s_slashers[slasher].config = config; // refill capacity SlasherState storage state = s_slashers[slasher].state; state.remainingSlashCapacityAmount = config.slashCapacity; state.lastSlashTimestamp = block.timestamp; emit SlasherConfigSet(slasher, config.refillRate, config.slashCapacity); } /// @notice Helper function to slash operators /// @param operators The list of operators to slash /// @param principalAmount The amount to slash from each operator's staked /// LINK amount /// @return The total amount slashed from all operators function _slashOperators( address[] calldata operators, uint256 principalAmount ) private returns (uint256) { // perform the slash on all operators and add up the total slashed amount uint256 totalSlashedAmount; Staker storage staker; uint256 totalPrincipal = s_pool.state.totalPrincipal; for (uint256 i; i < operators.length; ++i) { // verify input list is sorted and addresses are unique address operatorAddress = operators[i]; if (i < operators.length - 1 && operatorAddress >= operators[i + 1]) { revert InvalidOperatorList(); } staker = s_stakers[operatorAddress]; uint224 history = staker.history.latest(); uint256 operatorPrincipal = uint112(history >> 112); uint256 stakerStakedAtTime = uint112(history); uint256 slashedAmount = principalAmount > operatorPrincipal ? operatorPrincipal : principalAmount; uint256 updatedPrincipal = operatorPrincipal - slashedAmount; // update the staker's rewards s_rewardVault.updateReward(operatorAddress, operatorPrincipal); _updateStakerHistory({ staker: staker, latestPrincipal: updatedPrincipal, latestStakedAtTime: stakerStakedAtTime }); totalSlashedAmount += slashedAmount; totalPrincipal -= slashedAmount; emit Slashed(operatorAddress, slashedAmount, updatedPrincipal, totalPrincipal); } // update the pool state s_pool.state.totalPrincipal = totalPrincipal; return totalSlashedAmount; } /// @notice Helper function to reward the alerter /// @param alerter The alerter /// @param totalSlashedAmount The total amount slashed from all the operators /// @param alerterRewardAmount The amount to reward the alerter function _payAlerter( address alerter, uint256 totalSlashedAmount, uint256 alerterRewardAmount ) private { uint256 newAlerterRewardFunds = s_alerterRewardFunds + totalSlashedAmount; uint256 alerterRewardActual = newAlerterRewardFunds < alerterRewardAmount ? newAlerterRewardFunds : alerterRewardAmount; s_alerterRewardFunds = newAlerterRewardFunds - alerterRewardActual; // We emit an event here instead of reverting so that the alerter can // immediately receive a portion of their rewards. This event // will allow the contract manager to reimburse any remaining rewards to the // alerter. emit AlertingRewardPaid(alerter, alerterRewardActual, alerterRewardAmount); // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transfer(alerter, alerterRewardActual); } /// @notice Helper function to return the current remaining slash capacity for a slasher /// @param slasherConfig The slasher's config /// @param slasher The slasher /// @return The remaining slashing capacity function _getRemainingSlashCapacity( SlasherConfig memory slasherConfig, address slasher ) private view returns (uint256) { SlasherState memory slasherState = s_slashers[slasher].state; uint256 refilledAmount = (block.timestamp - slasherState.lastSlashTimestamp) * slasherConfig.refillRate; return Math.min( slasherConfig.slashCapacity, slasherState.remainingSlashCapacityAmount + refilledAmount ); } /// @dev Reverts if the msg.sender doesn't have the rewarder role. modifier onlyRewarder() { if (!hasRole(ALERT_REWARDER_ROLE, msg.sender)) { revert AccessForbidden(); } _; } /// @dev Reverts if not sent by an address that has the SLASHER role modifier onlySlasher() { if (!hasRole(SLASHER_ROLE, msg.sender)) { revert AccessForbidden(); } _; } /// @notice Checks that the maximum pool size is greater than or equal to /// the reserved space for operators. /// @param maxPoolSize The maximum pool size of the operator staking pool /// @param maxPrincipalPerStaker The maximum amount an operator can stake in the /// @param numOperators The number of operators in the pool /// @dev The reserved space is calculated by multiplying the number of /// operators and the maximum staked LINK amount per operator modifier validatePoolSpace( uint256 maxPoolSize, uint256 maxPrincipalPerStaker, uint256 numOperators ) { if (maxPoolSize < maxPrincipalPerStaker * numOperators) { revert InsufficientPoolSpace(maxPoolSize, maxPrincipalPerStaker, numOperators); } _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Checkpoints.sol) // This file was procedurally generated from scripts/generate/templates/Checkpoints.js. pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SafeCast.sol"; /** * @dev This library defines the `History` struct, for checkpointing values as they change at different points in * time, and later looking up past values by block number. See {Votes} as an example. * * To create a history of checkpoints define a variable type `Checkpoints.History` in your contract, and store a new * checkpoint for the current transaction block using the {push} function. * * _Available since v4.5._ */ library Checkpoints { struct History { Checkpoint[] _checkpoints; } struct Checkpoint { uint32 _blockNumber; uint224 _value; } /** * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one * before it is returned, or zero otherwise. Because the number returned corresponds to that at the end of the * block, the requested block number must be in the past, excluding the current block. */ function getAtBlock(History storage self, uint256 blockNumber) internal view returns (uint256) { require(blockNumber < block.number, "Checkpoints: block not yet mined"); uint32 key = SafeCast.toUint32(blockNumber); uint256 len = self._checkpoints.length; uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one * before it is returned, or zero otherwise. Similar to {upperLookup} but optimized for the case when the searched * checkpoint is probably "recent", defined as being among the last sqrt(N) checkpoints where N is the number of * checkpoints. */ function getAtProbablyRecentBlock(History storage self, uint256 blockNumber) internal view returns (uint256) { require(blockNumber < block.number, "Checkpoints: block not yet mined"); uint32 key = SafeCast.toUint32(blockNumber); uint256 len = self._checkpoints.length; uint256 low = 0; uint256 high = len; if (len > 5) { uint256 mid = len - Math.sqrt(len); if (key < _unsafeAccess(self._checkpoints, mid)._blockNumber) { high = mid; } else { low = mid + 1; } } uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Pushes a value onto a History so that it is stored as the checkpoint for the current block. * * Returns previous value and new value. */ function push(History storage self, uint256 value) internal returns (uint256, uint256) { return _insert(self._checkpoints, SafeCast.toUint32(block.number), SafeCast.toUint224(value)); } /** * @dev Pushes a value onto a History, by updating the latest value using binary operation `op`. The new value will * be set to `op(latest, delta)`. * * Returns previous value and new value. */ function push( History storage self, function(uint256, uint256) view returns (uint256) op, uint256 delta ) internal returns (uint256, uint256) { return push(self, op(latest(self), delta)); } /** * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. */ function latest(History storage self) internal view returns (uint224) { uint256 pos = self._checkpoints.length; return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value * in the most recent checkpoint. */ function latestCheckpoint( History storage self ) internal view returns (bool exists, uint32 _blockNumber, uint224 _value) { uint256 pos = self._checkpoints.length; if (pos == 0) { return (false, 0, 0); } else { Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); return (true, ckpt._blockNumber, ckpt._value); } } /** * @dev Returns the number of checkpoint. */ function length(History storage self) internal view returns (uint256) { return self._checkpoints.length; } /** * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, * or by updating the last one. */ function _insert(Checkpoint[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) { uint256 pos = self.length; if (pos > 0) { // Copying to memory is important here. Checkpoint memory last = _unsafeAccess(self, pos - 1); // Checkpoint keys must be non-decreasing. require(last._blockNumber <= key, "Checkpoint: decreasing keys"); // Update or push new checkpoint if (last._blockNumber == key) { _unsafeAccess(self, pos - 1)._value = value; } else { self.push(Checkpoint({_blockNumber: key, _value: value})); } return (last._value, value); } else { self.push(Checkpoint({_blockNumber: key, _value: value})); return (0, value); } } /** * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` if there is none. * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. * * WARNING: `high` should not be greater than the array's length. */ function _upperBinaryLookup( Checkpoint[] storage self, uint32 key, uint256 low, uint256 high ) private view returns (uint256) { while (low < high) { uint256 mid = Math.average(low, high); if (_unsafeAccess(self, mid)._blockNumber > key) { high = mid; } else { low = mid + 1; } } return high; } /** * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or `high` if there is none. * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. * * WARNING: `high` should not be greater than the array's length. */ function _lowerBinaryLookup( Checkpoint[] storage self, uint32 key, uint256 low, uint256 high ) private view returns (uint256) { while (low < high) { uint256 mid = Math.average(low, high); if (_unsafeAccess(self, mid)._blockNumber < key) { low = mid + 1; } else { high = mid; } } return high; } /** * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. */ function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private pure returns (Checkpoint storage result) { assembly { mstore(0, self.slot) result.slot := add(keccak256(0, 0x20), pos) } } struct Trace224 { Checkpoint224[] _checkpoints; } struct Checkpoint224 { uint32 _key; uint224 _value; } /** * @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint. * * Returns previous value and new value. */ function push(Trace224 storage self, uint32 key, uint224 value) internal returns (uint224, uint224) { return _insert(self._checkpoints, key, value); } /** * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if there is none. */ function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) { uint256 len = self._checkpoints.length; uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len); return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value; } /** * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none. */ function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) { uint256 len = self._checkpoints.length; uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none. * * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high keys). */ function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) { uint256 len = self._checkpoints.length; uint256 low = 0; uint256 high = len; if (len > 5) { uint256 mid = len - Math.sqrt(len); if (key < _unsafeAccess(self._checkpoints, mid)._key) { high = mid; } else { low = mid + 1; } } uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. */ function latest(Trace224 storage self) internal view returns (uint224) { uint256 pos = self._checkpoints.length; return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value * in the most recent checkpoint. */ function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) { uint256 pos = self._checkpoints.length; if (pos == 0) { return (false, 0, 0); } else { Checkpoint224 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); return (true, ckpt._key, ckpt._value); } } /** * @dev Returns the number of checkpoint. */ function length(Trace224 storage self) internal view returns (uint256) { return self._checkpoints.length; } /** * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, * or by updating the last one. */ function _insert(Checkpoint224[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) { uint256 pos = self.length; if (pos > 0) { // Copying to memory is important here. Checkpoint224 memory last = _unsafeAccess(self, pos - 1); // Checkpoint keys must be non-decreasing. require(last._key <= key, "Checkpoint: decreasing keys"); // Update or push new checkpoint if (last._key == key) { _unsafeAccess(self, pos - 1)._value = value; } else { self.push(Checkpoint224({_key: key, _value: value})); } return (last._value, value); } else { self.push(Checkpoint224({_key: key, _value: value})); return (0, value); } } /** * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` if there is none. * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. * * WARNING: `high` should not be greater than the array's length. */ function _upperBinaryLookup( Checkpoint224[] storage self, uint32 key, uint256 low, uint256 high ) private view returns (uint256) { while (low < high) { uint256 mid = Math.average(low, high); if (_unsafeAccess(self, mid)._key > key) { high = mid; } else { low = mid + 1; } } return high; } /** * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or `high` if there is none. * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. * * WARNING: `high` should not be greater than the array's length. */ function _lowerBinaryLookup( Checkpoint224[] storage self, uint32 key, uint256 low, uint256 high ) private view returns (uint256) { while (low < high) { uint256 mid = Math.average(low, high); if (_unsafeAccess(self, mid)._key < key) { low = mid + 1; } else { high = mid; } } return high; } /** * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. */ function _unsafeAccess( Checkpoint224[] storage self, uint256 pos ) private pure returns (Checkpoint224 storage result) { assembly { mstore(0, self.slot) result.slot := add(keccak256(0, 0x20), pos) } } struct Trace160 { Checkpoint160[] _checkpoints; } struct Checkpoint160 { uint96 _key; uint160 _value; } /** * @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint. * * Returns previous value and new value. */ function push(Trace160 storage self, uint96 key, uint160 value) internal returns (uint160, uint160) { return _insert(self._checkpoints, key, value); } /** * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if there is none. */ function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) { uint256 len = self._checkpoints.length; uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len); return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value; } /** * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none. */ function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) { uint256 len = self._checkpoints.length; uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none. * * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high keys). */ function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) { uint256 len = self._checkpoints.length; uint256 low = 0; uint256 high = len; if (len > 5) { uint256 mid = len - Math.sqrt(len); if (key < _unsafeAccess(self._checkpoints, mid)._key) { high = mid; } else { low = mid + 1; } } uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. */ function latest(Trace160 storage self) internal view returns (uint160) { uint256 pos = self._checkpoints.length; return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; } /** * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value * in the most recent checkpoint. */ function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) { uint256 pos = self._checkpoints.length; if (pos == 0) { return (false, 0, 0); } else { Checkpoint160 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); return (true, ckpt._key, ckpt._value); } } /** * @dev Returns the number of checkpoint. */ function length(Trace160 storage self) internal view returns (uint256) { return self._checkpoints.length; } /** * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, * or by updating the last one. */ function _insert(Checkpoint160[] storage self, uint96 key, uint160 value) private returns (uint160, uint160) { uint256 pos = self.length; if (pos > 0) { // Copying to memory is important here. Checkpoint160 memory last = _unsafeAccess(self, pos - 1); // Checkpoint keys must be non-decreasing. require(last._key <= key, "Checkpoint: decreasing keys"); // Update or push new checkpoint if (last._key == key) { _unsafeAccess(self, pos - 1)._value = value; } else { self.push(Checkpoint160({_key: key, _value: value})); } return (last._value, value); } else { self.push(Checkpoint160({_key: key, _value: value})); return (0, value); } } /** * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` if there is none. * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. * * WARNING: `high` should not be greater than the array's length. */ function _upperBinaryLookup( Checkpoint160[] storage self, uint96 key, uint256 low, uint256 high ) private view returns (uint256) { while (low < high) { uint256 mid = Math.average(low, high); if (_unsafeAccess(self, mid)._key > key) { high = mid; } else { low = mid + 1; } } return high; } /** * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or `high` if there is none. * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. * * WARNING: `high` should not be greater than the array's length. */ function _lowerBinaryLookup( Checkpoint160[] storage self, uint96 key, uint256 low, uint256 high ) private view returns (uint256) { while (low < high) { uint256 mid = Math.average(low, high); if (_unsafeAccess(self, mid)._key < key) { low = mid + 1; } else { high = mid; } } return high; } /** * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. */ function _unsafeAccess( Checkpoint160[] storage self, uint256 pos ) private pure returns (Checkpoint160 storage result) { assembly { mstore(0, self.slot) result.slot := add(keccak256(0, 0x20), pos) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControlDefaultAdminRules.sol) pragma solidity ^0.8.0; import "./AccessControl.sol"; import "./IAccessControlDefaultAdminRules.sol"; import "../utils/math/SafeCast.sol"; import "../interfaces/IERC5313.sol"; /** * @dev Extension of {AccessControl} that allows specifying special rules to manage * the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions * over other roles that may potentially have privileged rights in the system. * * If a specific role doesn't have an admin role assigned, the holder of the * `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it. * * This contract implements the following risk mitigations on top of {AccessControl}: * * * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced. * * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account. * * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted. * * The delay can be changed by scheduling, see {changeDefaultAdminDelay}. * * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`. * * Example usage: * * ```solidity * contract MyToken is AccessControlDefaultAdminRules { * constructor() AccessControlDefaultAdminRules( * 3 days, * msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder * ) {} * } * ``` * * _Available since v4.9._ */ abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl { // pending admin pair read/written together frequently address private _pendingDefaultAdmin; uint48 private _pendingDefaultAdminSchedule; // 0 == unset uint48 private _currentDelay; address private _currentDefaultAdmin; // pending delay pair read/written together frequently uint48 private _pendingDelay; uint48 private _pendingDelaySchedule; // 0 == unset /** * @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address. */ constructor(uint48 initialDelay, address initialDefaultAdmin) { require(initialDefaultAdmin != address(0), "AccessControl: 0 default admin"); _currentDelay = initialDelay; _grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin); } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC5313-owner}. */ function owner() public view virtual returns (address) { return defaultAdmin(); } /// /// Override AccessControl role management /// /** * @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`. */ function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly grant default admin role"); super.grantRole(role, account); } /** * @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`. */ function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly revoke default admin role"); super.revokeRole(role, account); } /** * @dev See {AccessControl-renounceRole}. * * For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling * {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule * has also passed when calling this function. * * After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions. * * NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin}, * thereby disabling any functionality that is only available for it, and the possibility of reassigning a * non-administrated role. */ function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) { if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) { (address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin(); require( newDefaultAdmin == address(0) && _isScheduleSet(schedule) && _hasSchedulePassed(schedule), "AccessControl: only can renounce in two delayed steps" ); delete _pendingDefaultAdminSchedule; } super.renounceRole(role, account); } /** * @dev See {AccessControl-_grantRole}. * * For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the * role has been previously renounced. * * NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE` * assignable again. Make sure to guarantee this is the expected behavior in your implementation. */ function _grantRole(bytes32 role, address account) internal virtual override { if (role == DEFAULT_ADMIN_ROLE) { require(defaultAdmin() == address(0), "AccessControl: default admin already granted"); _currentDefaultAdmin = account; } super._grantRole(role, account); } /** * @dev See {AccessControl-_revokeRole}. */ function _revokeRole(bytes32 role, address account) internal virtual override { if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) { delete _currentDefaultAdmin; } super._revokeRole(role, account); } /** * @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override { require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't violate default admin rules"); super._setRoleAdmin(role, adminRole); } /// /// AccessControlDefaultAdminRules accessors /// /** * @inheritdoc IAccessControlDefaultAdminRules */ function defaultAdmin() public view virtual returns (address) { return _currentDefaultAdmin; } /** * @inheritdoc IAccessControlDefaultAdminRules */ function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) { return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule); } /** * @inheritdoc IAccessControlDefaultAdminRules */ function defaultAdminDelay() public view virtual returns (uint48) { uint48 schedule = _pendingDelaySchedule; return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay; } /** * @inheritdoc IAccessControlDefaultAdminRules */ function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) { schedule = _pendingDelaySchedule; return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0); } /** * @inheritdoc IAccessControlDefaultAdminRules */ function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) { return 5 days; } /// /// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin /// /** * @inheritdoc IAccessControlDefaultAdminRules */ function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) { _beginDefaultAdminTransfer(newAdmin); } /** * @dev See {beginDefaultAdminTransfer}. * * Internal function without access restriction. */ function _beginDefaultAdminTransfer(address newAdmin) internal virtual { uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay(); _setPendingDefaultAdmin(newAdmin, newSchedule); emit DefaultAdminTransferScheduled(newAdmin, newSchedule); } /** * @inheritdoc IAccessControlDefaultAdminRules */ function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) { _cancelDefaultAdminTransfer(); } /** * @dev See {cancelDefaultAdminTransfer}. * * Internal function without access restriction. */ function _cancelDefaultAdminTransfer() internal virtual { _setPendingDefaultAdmin(address(0), 0); } /** * @inheritdoc IAccessControlDefaultAdminRules */ function acceptDefaultAdminTransfer() public virtual { (address newDefaultAdmin, ) = pendingDefaultAdmin(); require(_msgSender() == newDefaultAdmin, "AccessControl: pending admin must accept"); _acceptDefaultAdminTransfer(); } /** * @dev See {acceptDefaultAdminTransfer}. * * Internal function without access restriction. */ function _acceptDefaultAdminTransfer() internal virtual { (address newAdmin, uint48 schedule) = pendingDefaultAdmin(); require(_isScheduleSet(schedule) && _hasSchedulePassed(schedule), "AccessControl: transfer delay not passed"); _revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin()); _grantRole(DEFAULT_ADMIN_ROLE, newAdmin); delete _pendingDefaultAdmin; delete _pendingDefaultAdminSchedule; } /// /// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay /// /** * @inheritdoc IAccessControlDefaultAdminRules */ function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) { _changeDefaultAdminDelay(newDelay); } /** * @dev See {changeDefaultAdminDelay}. * * Internal function without access restriction. */ function _changeDefaultAdminDelay(uint48 newDelay) internal virtual { uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay); _setPendingDelay(newDelay, newSchedule); emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule); } /** * @inheritdoc IAccessControlDefaultAdminRules */ function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) { _rollbackDefaultAdminDelay(); } /** * @dev See {rollbackDefaultAdminDelay}. * * Internal function without access restriction. */ function _rollbackDefaultAdminDelay() internal virtual { _setPendingDelay(0, 0); } /** * @dev Returns the amount of seconds to wait after the `newDelay` will * become the new {defaultAdminDelay}. * * The value returned guarantees that if the delay is reduced, it will go into effect * after a wait that honors the previously set delay. * * See {defaultAdminDelayIncreaseWait}. */ function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) { uint48 currentDelay = defaultAdminDelay(); // When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up // to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day // to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new // delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like // using milliseconds instead of seconds. // // When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees // that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled. // For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days. return newDelay > currentDelay ? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48 : currentDelay - newDelay; } /// /// Private setters /// /** * @dev Setter of the tuple for pending admin and its schedule. * * May emit a DefaultAdminTransferCanceled event. */ function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private { (, uint48 oldSchedule) = pendingDefaultAdmin(); _pendingDefaultAdmin = newAdmin; _pendingDefaultAdminSchedule = newSchedule; // An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted. if (_isScheduleSet(oldSchedule)) { // Emit for implicit cancellations when another default admin was scheduled. emit DefaultAdminTransferCanceled(); } } /** * @dev Setter of the tuple for pending delay and its schedule. * * May emit a DefaultAdminDelayChangeCanceled event. */ function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private { uint48 oldSchedule = _pendingDelaySchedule; if (_isScheduleSet(oldSchedule)) { if (_hasSchedulePassed(oldSchedule)) { // Materialize a virtual delay _currentDelay = _pendingDelay; } else { // Emit for implicit cancellations when another delay was scheduled. emit DefaultAdminDelayChangeCanceled(); } } _pendingDelay = newDelay; _pendingDelaySchedule = newSchedule; } /// /// Private helpers /// /** * @dev Defines if an `schedule` is considered set. For consistency purposes. */ function _isScheduleSet(uint48 schedule) private pure returns (bool) { return schedule != 0; } /** * @dev Defines if an `schedule` is considered passed. For consistency purposes. */ function _hasSchedulePassed(uint48 schedule) private view returns (bool) { return schedule < block.timestamp; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; interface IPausable { /// @notice This function pauses the contract /// @dev Sets the pause flag to true function emergencyPause() external; /// @notice This function unpauses the contract /// @dev Sets the pause flag to false function emergencyUnpause() external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.2) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} * * _Available since v4.7._ */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} * * _Available since v4.7._ */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). * * _Available since v4.7._ */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof"); // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { require(proofPos == proofLen, "MerkleProof: invalid multiproof"); unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof"); // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { require(proofPos == proofLen, "MerkleProof: invalid multiproof"); unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; interface IMerkleAccessController { /// @notice Emitted when the contract owner updates the staking allowlist /// @param oldMerkleRoot The root of the old Staking allowlist merkle tree /// @param newMerkleRoot The root of a new Staking allowlist merkle tree event MerkleRootChanged(bytes32 oldMerkleRoot, bytes32 newMerkleRoot); /// @notice Validates if a community staker has access to the private staking pool /// @param staker The community staker's address /// @param proof Merkle proof for the community staker's allowlist /// @return true If the staker has access to the private staking pool function hasAccess(address staker, bytes32[] calldata proof) external view returns (bool); /// @notice This function is called to update the staking allowlist in a private staking pool /// @dev Only callable by the contract owner /// @param newMerkleRoot Merkle Tree root, used to prove access for community stakers /// will be required at opening but can be removed at any time by the owner when /// staking access will be granted to the public. function setMerkleRoot(bytes32 newMerkleRoot) external; /// @notice This function returns the current root of the Staking allowlist merkle tree /// @return The current root of the Staking allowlist merkle tree function getMerkleRoot() external view returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {ERC677ReceiverInterface} from "@chainlink/contracts/src/v0.8/interfaces/ERC677ReceiverInterface.sol"; import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol"; import {Checkpoints} from "@openzeppelin/contracts/utils/Checkpoints.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {IMigratable} from "../interfaces/IMigratable.sol"; import {IRewardVault} from "../interfaces/IRewardVault.sol"; import {IStakingOwner} from "../interfaces/IStakingOwner.sol"; import {IStakingPool} from "../interfaces/IStakingPool.sol"; import {Migratable} from "../Migratable.sol"; import {PausableWithAccessControl} from "../PausableWithAccessControl.sol"; /// @notice This contract is the base contract for staking pools. Each staking pool extends this /// contract. /// @dev This contract is abstract and must be inherited. /// @dev invariant maxPoolSize must be greater than or equal to the totalPrincipal. /// @dev invariant maxPoolSize must be greater than or equal to the maxPrincipalPerStaker. /// @dev invariant contract's LINK token balance should be greater than or equal to the /// totalPrincipal. /// @dev invariant The migrated staked LINK amount must be less than or equal to the staker's staked /// LINK amount + /// rewards from the v0.1 staking pool. /// @dev invariant The migrated staked LINK amount must be less than or equal to the /// maxPrincipalPerStaker. /// @dev We only support LINK token in v0.2 staking. Rebasing tokens, ERC777 tokens, fee-on-transfer /// tokens or tokens that do not have 18 decimal places are not supported. abstract contract StakingPoolBase is ERC677ReceiverInterface, IStakingPool, IStakingOwner, Migratable, PausableWithAccessControl { using Checkpoints for Checkpoints.History; using SafeCast for uint256; /// @notice This error is thrown when the staking pool is not active. error PoolNotActive(); /// @notice This error is thrown when the unbonding period is set to 0 error InvalidUnbondingPeriod(); /// @notice This error is thrown when the claim period is set to 0 error InvalidClaimPeriod(); /// @notice This error is thrown whenever a staker tries to unbond during /// their unbonding period. /// @param unbondingPeriodEndsAt The time the unbonding period is finished error UnbondingOrClaimPeriodActive(uint256 unbondingPeriodEndsAt); /// @notice This error is thrown whenever a staker tries to unstake outside /// the claim period /// @param staker The staker trying to unstake error StakerNotInClaimPeriod(address staker); /// @notice This error is thrown when an invalid claim period range is provided /// @param minClaimPeriod The min claim period /// @param maxClaimPeriod The max claim period error InvalidClaimPeriodRange(uint256 minClaimPeriod, uint256 maxClaimPeriod); /// @notice This error is thrown when an invalid max unbonding period is provided /// @param maxUnbondingPeriod The max unbonding period error InvalidMaxUnbondingPeriod(uint256 maxUnbondingPeriod); /// @notice This error is thrown when a staker tries to stake and the reward vault connected to /// this pool is not open or is paused error RewardVaultNotActive(); /// @notice This error is thrown when admin tries to open the pool and the reward vault connected /// to this pool has not had rewards added to it. error RewardVaultHasNoRewards(); /// @notice This error is thrown when admin tries to set a new reward vault and the old reward /// vault is not closed yet. error RewardVaultNotClosed(); /// @notice This event is emitted whenever a staker initiates the unbonding /// period. /// @param staker The staker that has started their unbonding period. event UnbondingPeriodStarted(address indexed staker); /// @notice This event is emitted when a staker's unbonding period is reset /// @param staker The staker that has reset their unbonding period event UnbondingPeriodReset(address indexed staker); /// @notice This event is emitted when the unbonding period has been changed /// @param oldUnbondingPeriod The old unbonding period /// @param newUnbondingPeriod The new unbonding period event UnbondingPeriodSet(uint256 oldUnbondingPeriod, uint256 newUnbondingPeriod); /// @notice This event is emitted when the claim period is set /// @param oldClaimPeriod The old claim period /// @param newClaimPeriod The new claim period event ClaimPeriodSet(uint256 oldClaimPeriod, uint256 newClaimPeriod); /// @notice This event is emitted when the reward vault is set /// @param oldRewardVault The old reward vault /// @param newRewardVault The new reward vault event RewardVaultSet(address indexed oldRewardVault, address indexed newRewardVault); /// @notice This event is emitted when the staker is migrated to the migration target /// @param migrationTarget The migration target /// @param amount The staker's staked LINK amount that was migrated in juels /// @param migrationData The migration data event StakerMigrated(address indexed migrationTarget, uint256 amount, bytes migrationData); /// @notice This struct defines the params required by the Staking contract's /// constructor. struct ConstructorParamsBase { /// @notice The LINK Token LinkTokenInterface LINKAddress; /// @notice The initial maximum total stake amount for all stakers in the /// pool uint96 initialMaxPoolSize; /// @notice The initial maximum stake amount for a staker uint96 initialMaxPrincipalPerStaker; /// @notice The minimum stake amount that a staker must stake uint96 minPrincipalPerStaker; /// @notice The initial unbonding period uint32 initialUnbondingPeriod; /// @notice The max value that the unbonding period can be set to uint32 maxUnbondingPeriod; /// @notice The initial claim period uint32 initialClaimPeriod; /// @notice The min value that the claim period can be set to uint32 minClaimPeriod; /// @notice The max value that the claim period can be set to uint32 maxClaimPeriod; /// @notice The time it requires to transfer admin role uint48 adminRoleTransferDelay; } /// @notice This struct defines the params that the pool is configured with struct PoolConfigs { /// @notice The max amount of staked LINK allowed in the pool in juels. The max value of this /// field is expected to be less than 1 billion (10^9 * 10^18), which is less than the max value /// that can be represented by a uint96 (~7.9*10^28). uint96 maxPoolSize; /// @notice The max amount of LINK a staker can stake in juels. The max value of this field is /// expected to be less than 1 million (10^6 * 10^18), which is less than the max value that can /// be represented by a uint96 (~7.9*10^28). uint96 maxPrincipalPerStaker; /// @notice The length of the unbonding period in seconds. The max value of this field is /// expected to be less than a year, or 30 million (3.2*10^7), which is less than the max value /// that can be represented by a uint32 (~4.2*10^9). uint32 unbondingPeriod; /// @notice The length of the claim period in seconds. The max value of this field is /// expected to be less than a year, or 30 million (3.2*10^7), which is less than the max value /// that can be represented by a uint32 (~4.2*10^9). uint32 claimPeriod; } /// @notice This struct defines the state of the staking pool struct PoolState { /// @notice The total staked LINK amount amount in the pool uint256 totalPrincipal; /// @notice The time that the pool was closed uint256 closedAt; } /// @notice This struct defines the global state and configuration of the pool struct Pool { /// @notice The pool's configuration PoolConfigs configs; /// @notice The pool's state PoolState state; } /// @notice This is the ID for the initiator role, which is given to the /// addresses that will add open the pools, and set the merkle root for the community pool. /// @dev Hash: 6b8b15f1c11543d8280deaa7c24d12fffba6a357e4428e8c43e4234790186bff bytes32 public constant INITIATOR_ROLE = keccak256("INITIATOR_ROLE"); /// @notice The LINK token LinkTokenInterface internal immutable i_LINK; /// @notice The staking pool state and configuration Pool internal s_pool; /// @notice Mapping of a staker's address to their staker state mapping(address staker => IStakingPool.Staker) internal s_stakers; /// @notice Migration proxy address address internal s_migrationProxy; /// @notice The latest reward vault address IRewardVault internal s_rewardVault; /// @notice The min amount of LINK that a staker can stake uint96 internal immutable i_minPrincipalPerStaker; /// @notice The min value that the claim period can be set to uint32 private immutable i_minClaimPeriod; /// @notice The max value that the claim period can be set to uint32 private immutable i_maxClaimPeriod; /// @notice The max value that the unbonding period can be set to uint32 private immutable i_maxUnbondingPeriod; /// @notice Flag that signals if the staking pool is open for staking bool internal s_isOpen; constructor(ConstructorParamsBase memory params) PausableWithAccessControl(params.adminRoleTransferDelay, msg.sender) { if (address(params.LINKAddress) == address(0)) revert InvalidZeroAddress(); if (params.minPrincipalPerStaker == 0) revert InvalidMinStakeAmount(); if (params.minPrincipalPerStaker >= params.initialMaxPrincipalPerStaker) { revert InvalidMinStakeAmount(); } if (params.maxUnbondingPeriod == 0) { revert InvalidMaxUnbondingPeriod(params.maxUnbondingPeriod); } if (params.minClaimPeriod == 0 || params.minClaimPeriod >= params.maxClaimPeriod) { revert InvalidClaimPeriodRange(params.minClaimPeriod, params.maxClaimPeriod); } i_LINK = params.LINKAddress; i_minPrincipalPerStaker = params.minPrincipalPerStaker; i_maxUnbondingPeriod = params.maxUnbondingPeriod; _setUnbondingPeriod(params.initialUnbondingPeriod); _setPoolConfig(params.initialMaxPoolSize, params.initialMaxPrincipalPerStaker); i_minClaimPeriod = params.minClaimPeriod; i_maxClaimPeriod = params.maxClaimPeriod; _setClaimPeriod(params.initialClaimPeriod); } /// @inheritdoc IMigratable /// @dev This will migrate the staker's staked LINK /// @dev precondition This contract must be closed and upgraded to a new pool. /// @dev precondition The migration target must be set. /// @dev precondition The caller must be staked in the pool. function migrate(bytes calldata data) external whenClosed validateMigrationTargetSet { // must be in storage to get access to latest() IStakingPool.Staker storage staker = s_stakers[msg.sender]; uint224 history = staker.history.latest(); uint112 stakerPrincipal = uint112(history >> 112); uint112 stakerStakedAtTime = uint112(history); if (stakerPrincipal == 0) revert StakeNotFound(msg.sender); bytes memory migrationData = abi.encode(msg.sender, stakerStakedAtTime, data); // Finalize staker's rewards to include any rewards they have earned before resetting the // principal and stakedAtTime. s_rewardVault.concludeRewardPeriod({ staker: msg.sender, oldPrincipal: stakerPrincipal, stakedAt: stakerStakedAtTime, unstakedAmount: stakerPrincipal, shouldForfeit: false }); s_pool.state.totalPrincipal -= stakerPrincipal; // do not reset staked at time to not reset the multiplier because staker is not forfeiting // rewards when migrating _updateStakerHistory({ staker: staker, latestPrincipal: 0, latestStakedAtTime: stakerStakedAtTime }); // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transferAndCall({to: s_migrationTarget, value: stakerPrincipal, data: migrationData}); emit StakerMigrated(s_migrationTarget, stakerPrincipal, migrationData); } /// @notice Starts the unbonding period for the staker. A staker may unstake /// their staked LINK during the claim period that follows the unbonding period. /// @dev precondition The caller must be staked in the pool. /// @dev precondition The caller must not be in an unbonding period. /// @dev precondition The caller must not be in a claim period. function unbond() external virtual { Staker storage staker = s_stakers[msg.sender]; uint224 history = staker.history.latest(); uint112 stakerPrincipal = uint112(history >> 112); if (stakerPrincipal == 0) revert StakeNotFound(msg.sender); _unbond(staker); } /// @notice Sets the new unbonding period for the pool. Stakers that are /// already unbonding will not be affected. /// @param newUnbondingPeriod The new unbonding period /// @dev precondition The caller must have the default admin role. /// @dev precondition Cannot be called after the pool is closed. function setUnbondingPeriod(uint256 newUnbondingPeriod) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing { _setUnbondingPeriod(newUnbondingPeriod); } /// @notice Returns the max unbonding period /// @return uint256 The max value that the unbonding period can be set to function getMaxUnbondingPeriod() external view returns (uint256) { return (i_maxUnbondingPeriod); } /// @notice Set the claim period /// @param claimPeriod The claim period /// @dev precondition Cannot be called after the pool is closed. function setClaimPeriod(uint256 claimPeriod) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing { _setClaimPeriod(claimPeriod); } /// @notice Sets the new reward vault for the pool /// @param newRewardVault The new reward vault /// @dev precondition The caller must have the default admin role. /// @dev precondition Cannot be called after the pool is closed. function setRewardVault(IRewardVault newRewardVault) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing { if (address(newRewardVault) == address(0)) revert InvalidZeroAddress(); address oldRewardVault = address(s_rewardVault); if (oldRewardVault == address(newRewardVault)) return; if (address(s_rewardVault) != address(0) && s_rewardVault.isOpen()) { revert RewardVaultNotClosed(); } if ( address(s_rewardVault) != address(0) && (!newRewardVault.isOpen() || newRewardVault.isPaused()) ) revert RewardVaultNotActive(); if (address(s_rewardVault) != address(0) && !newRewardVault.hasRewardAdded()) { revert RewardVaultHasNoRewards(); } s_rewardVault = newRewardVault; emit RewardVaultSet(oldRewardVault, address(newRewardVault)); } /// @notice LINK transfer callback function called when transferAndCall is called with this /// contract as a target. /// @param sender staker's address if they stake into the pool by calling transferAndCall on the /// LINK token, or MigrationProxy contract when a staker migrates from V0.1 to V0.2 /// @param amount Amount of LINK token transferred /// @param data Bytes data received, represents migration path /// @inheritdoc ERC677ReceiverInterface /// @dev precondition The migration proxy must be set. /// @dev precondition This contract must be open and not paused. /// @dev precondition The reward vault must be open and not paused. function onTokenTransfer( address sender, uint256 amount, bytes calldata data ) external validateFromLINK validateMigrationProxySet whenOpen whenRewardVaultOpen whenNotPaused { if (amount == 0) return; // Check if this call was forwarded from the migration proxy. address staker = sender == s_migrationProxy ? _getStakerAddress(data) : sender; if (staker == address(0)) revert InvalidZeroAddress(); // includes access check for non migration proxy _validateOnTokenTransfer(sender, staker, data); Staker storage stakerState = s_stakers[staker]; uint224 history = stakerState.history.latest(); uint256 stakerPrincipal = uint256(history >> 112); uint256 stakedAt = uint112(history); _resetUnbondingPeriod(stakerState, staker); s_rewardVault.concludeRewardPeriod({ staker: staker, oldPrincipal: stakerPrincipal, unstakedAmount: 0, shouldForfeit: false, stakedAt: stakedAt }); _increaseStake(staker, stakerPrincipal + amount, amount); } /// @notice Returns the minimum and maximum claim periods that can be set by the owner /// @return uint256 minimum claim period /// @return uint256 maximum claim period function getClaimPeriodLimits() external view returns (uint256, uint256) { return (i_minClaimPeriod, i_maxClaimPeriod); } // ================= // IStakingOwner // ================= /// @inheritdoc IStakingOwner /// @dev precondition The caller must have the default admin role. function setPoolConfig( uint256 maxPoolSize, uint256 maxPrincipalPerStaker ) external virtual onlyRole(DEFAULT_ADMIN_ROLE) whenOpen { _setPoolConfig(maxPoolSize, maxPrincipalPerStaker); } /// @inheritdoc IStakingOwner /// @dev precondition The caller must have the initiator role. function open() external onlyRole(INITIATOR_ROLE) whenBeforeOpening validateRewardVaultSet whenRewardVaultOpen whenRewardVaultHasRewards { _validateBeforeOpen(); s_isOpen = true; emit PoolOpened(); } /// @inheritdoc IStakingOwner /// @dev precondition The caller must have the default admin role. function close() external onlyRole(DEFAULT_ADMIN_ROLE) whenOpen { s_isOpen = false; s_pool.state.closedAt = block.timestamp; emit PoolClosed(); } /// @inheritdoc IStakingOwner /// @dev precondition The caller must have the default admin role. function setMigrationProxy(address migrationProxy) external onlyRole(DEFAULT_ADMIN_ROLE) whenBeforeClosing { if (migrationProxy == address(0)) revert InvalidZeroAddress(); if (s_migrationProxy == migrationProxy) return; address oldMigrationProxy = s_migrationProxy; s_migrationProxy = migrationProxy; emit MigrationProxySet(oldMigrationProxy, migrationProxy); } // ================= // IStakingPool // ================= /// @inheritdoc IStakingPool /// @dev precondition The caller must be staked in the pool. /// @dev precondition The caller must be in the claim period or the pool must be closed or paused. /// @dev There is a possible reentrancy attack here where a malicious admin /// can point this pool to a malicious reward vault that calls unstake on the /// pool again. This reentrancy attack is possible as the pool updates the /// staker's staked LINK amount after it calls concludeRewardPeriod on the configured reward /// vault. This scenario is mitigated by forcing the admin to go through /// a timelock period that is longer than the unbonding period, which will /// provide stakers sufficient time to withdraw their staked LINK from the /// pool before a malicious reward vault is set. function unstake(uint256 amount) external { // cannot unstake 0 if (amount == 0) revert UnstakeZeroAmount(); Staker storage staker = s_stakers[msg.sender]; if (!_canUnstake(staker)) { revert StakerNotInClaimPeriod(msg.sender); } uint224 history = staker.history.latest(); uint256 stakerPrincipal = uint256(history >> 112); uint256 stakedAt = uint112(history); // verify that the staker has enough staked LINK amount to unstake if (amount > stakerPrincipal) revert UnstakeExceedsPrincipal(); uint256 updatedPrincipal = stakerPrincipal - amount; // in the case of a partial withdrawal, verify new staked LINK amount is above minimum if (amount < stakerPrincipal && updatedPrincipal < i_minPrincipalPerStaker) { revert UnstakePrincipalBelowMinAmount(); } s_rewardVault.concludeRewardPeriod({ staker: msg.sender, oldPrincipal: stakerPrincipal, unstakedAmount: amount, shouldForfeit: true, stakedAt: stakedAt }); s_pool.state.totalPrincipal -= amount; // Reset the staker's staked at time to 0 to prevent the multiplier // from growing if the staker has unstaked all their staked LINK _updateStakerHistory({ staker: staker, latestPrincipal: updatedPrincipal, latestStakedAtTime: updatedPrincipal == 0 ? 0 : block.timestamp }); // The return value is not checked since the call will revert if any balance, allowance or // receiver conditions fail. i_LINK.transfer(msg.sender, amount); emit Unstaked(msg.sender, amount, updatedPrincipal, s_pool.state.totalPrincipal); } /// @inheritdoc IStakingPool function getTotalPrincipal() external view returns (uint256) { return s_pool.state.totalPrincipal; } /// @inheritdoc IStakingPool function getStakerPrincipal(address staker) external view returns (uint256) { return uint112(s_stakers[staker].history.latest() >> 112); } /// @inheritdoc IStakingPool function getStakerPrincipalAt( address staker, uint256 blockNumber ) external view returns (uint256) { // `Checkpoints` requires to exclude the current block when calling `getAtBlock` return (blockNumber == block.number) ? uint112(s_stakers[staker].history.latest() >> 112) : uint112(s_stakers[staker].history.getAtBlock(blockNumber) >> 112); } /// @inheritdoc IStakingPool function getStakerStakedAtTime(address staker) external view returns (uint256) { return uint112(s_stakers[staker].history.latest()); } /// @inheritdoc IStakingPool function getStakerStakedAtTimeAt( address staker, uint256 blockNumber ) external view returns (uint256) { // `Checkpoints` requires to exclude the current block when calling `getAtBlock` return (blockNumber == block.number) ? uint112(s_stakers[staker].history.latest()) : uint112(s_stakers[staker].history.getAtBlock(blockNumber)); } /// @inheritdoc IStakingPool function getRewardVault() external view returns (IRewardVault) { return s_rewardVault; } /// @inheritdoc IStakingPool function getChainlinkToken() external view returns (address) { return address(i_LINK); } /// @inheritdoc IStakingPool function getMigrationProxy() external view returns (address) { return s_migrationProxy; } /// @inheritdoc IStakingPool function isOpen() external view returns (bool) { return s_isOpen; } /// @inheritdoc IStakingPool function isActive() external view returns (bool) { return _isActive(); } /// @inheritdoc IStakingPool function getStakerLimits() external view returns (uint256, uint256) { return (i_minPrincipalPerStaker, s_pool.configs.maxPrincipalPerStaker); } /// @inheritdoc IStakingPool function getMaxPoolSize() external view returns (uint256) { return s_pool.configs.maxPoolSize; } /// @notice Returns the time a staker's unbonding period ends /// @param staker The address of the staker to query /// @return uint256 The timestamp of when the staker's unbonding period ends. /// This value will be 0 if the unbonding period is not active. function getUnbondingEndsAt(address staker) external view returns (uint256) { return s_stakers[staker].unbondingPeriodEndsAt; } /// @notice Returns the pool's unbonding parameters /// @return uint256 The pool's unbonding period /// @return uint256 The pools's claim period function getUnbondingParams() external view returns (uint256, uint256) { return (s_pool.configs.unbondingPeriod, s_pool.configs.claimPeriod); } /// @notice Returns the time a staker's claim period ends /// @param staker The staker trying to unstake their staked LINK /// @return uint256 The timestamp of when the staker's claim period ends. /// This value will be 0 if the unbonding period has not started. function getClaimPeriodEndsAt(address staker) external view returns (uint256) { return s_stakers[staker].claimPeriodEndsAt; } // =============== // ERC165 // =============== /// @notice This function allows the calling contract to /// check if the contract deployed at this address is a valid /// LINKTokenReceiver. A contract is a valid LINKTokenReceiver /// if it implements the onTokenTransfer function. /// @param interfaceID The ID of the interface to check against /// @return bool True if the contract is a valid LINKTokenReceiver. function supportsInterface(bytes4 interfaceID) public view override returns (bool) { return interfaceID == this.onTokenTransfer.selector || super.supportsInterface(interfaceID); } // ========= // Helpers // ========= /// @notice Resets a staker's unbonding period /// @param stakerState The staker's current state /// @param staker The address of the staker to reset the unbonding period for /// @dev This sets the stakerState's unbondingPeriodEndsAt and /// claimPeriodEndsAt to 0 function _resetUnbondingPeriod(Staker storage stakerState, address staker) internal { if (stakerState.unbondingPeriodEndsAt != 0) { delete stakerState.unbondingPeriodEndsAt; delete stakerState.claimPeriodEndsAt; emit UnbondingPeriodReset(staker); } } /// @inheritdoc Migratable /// @dev precondition The migration target must implement the onTokenTransfer function. /// @dev precondition Cannot be called after the pool is closed. function _validateMigrationTarget(address newMigrationTarget) internal override whenBeforeClosing { Migratable._validateMigrationTarget(newMigrationTarget); if ( !IERC165(newMigrationTarget).supportsInterface( ERC677ReceiverInterface.onTokenTransfer.selector ) ) { revert InvalidMigrationTarget(); } } /// @notice Validate for when LINK is staked or migrated into the pool /// @param sender The address transferring LINK into the pool. Could be the migration proxy /// contract or the staker. /// @param staker The address staking or migrating LINK into the pool /// @param data Arbitrary data passed when staking or migrating function _validateOnTokenTransfer( address sender, address staker, bytes calldata data ) internal view virtual; /// @notice Validates pool state before opening function _validateBeforeOpen() internal view virtual; /// @notice Util function for setting the pool config /// @param maxPoolSize The max amount of staked LINK allowed in the pool /// @param maxPrincipalPerStaker The max amount of LINK a staker can stake /// in the pool. function _setPoolConfig(uint256 maxPoolSize, uint256 maxPrincipalPerStaker) internal { PoolConfigs storage configs = s_pool.configs; // only allow increasing the maxPoolSize if (maxPoolSize == 0 || maxPoolSize < configs.maxPoolSize) { revert InvalidPoolSize(maxPoolSize); } // only allow increasing the maxPrincipalPerStaker if ( maxPrincipalPerStaker == 0 || maxPrincipalPerStaker > maxPoolSize || configs.maxPrincipalPerStaker > maxPrincipalPerStaker ) revert InvalidMaxStakeAmount(maxPrincipalPerStaker); if (configs.maxPoolSize != maxPoolSize) { configs.maxPoolSize = maxPoolSize.toUint96(); emit PoolSizeIncreased(maxPoolSize); } if (configs.maxPrincipalPerStaker != maxPrincipalPerStaker) { configs.maxPrincipalPerStaker = maxPrincipalPerStaker.toUint96(); emit MaxPrincipalAmountIncreased(maxPrincipalPerStaker); } } /// @notice Util function for setting the unbonding period /// @param unbondingPeriod The unbonding period function _setUnbondingPeriod(uint256 unbondingPeriod) internal { if (unbondingPeriod == 0 || unbondingPeriod > i_maxUnbondingPeriod) { revert InvalidUnbondingPeriod(); } if (s_pool.configs.unbondingPeriod == unbondingPeriod) return; uint256 oldUnbondingPeriod = s_pool.configs.unbondingPeriod; s_pool.configs.unbondingPeriod = unbondingPeriod.toUint32(); emit UnbondingPeriodSet(oldUnbondingPeriod, unbondingPeriod); } /// @notice Updates the staking pool state and the staker state /// @param sender The staker address /// @param newPrincipal The staker's staked LINK amount after staking /// @param amount The amount to stake function _increaseStake(address sender, uint256 newPrincipal, uint256 amount) internal { Staker storage staker = s_stakers[sender]; // validate staking limits if (newPrincipal < i_minPrincipalPerStaker) { revert InsufficientStakeAmount(); } if (newPrincipal > s_pool.configs.maxPrincipalPerStaker) { revert ExceedsMaxStakeAmount(); } uint256 newTotalPrincipal = s_pool.state.totalPrincipal + amount; if (newTotalPrincipal > s_pool.configs.maxPoolSize) { revert ExceedsMaxPoolSize(); } // update the pool state s_pool.state.totalPrincipal = newTotalPrincipal; // update the staker state _updateStakerHistory({ staker: staker, latestPrincipal: newPrincipal, latestStakedAtTime: block.timestamp }); emit Staked(sender, amount, newPrincipal, newTotalPrincipal); } /// @notice Gets the staker address from the data passed by the MigrationProxy contract /// @param data The data passed by the MigrationProxy contract /// @return The staker address function _getStakerAddress(bytes calldata data) internal pure returns (address) { if (data.length == 0) revert InvalidData(); // decode the data (address staker) = abi.decode(data, (address)); return staker; } /// @notice Checks to see whether or not a staker is eligible to /// unstake their staked LINK amount (when the pool is closed or, when the pool is open and they /// are in the claim period or, when pool is paused) /// @param staker The staker trying to unstake their staked LINK /// @return bool True if the staker is eligible to unstake function _canUnstake(Staker storage staker) internal view returns (bool) { return s_pool.state.closedAt != 0 || _inClaimPeriod(staker) || paused(); } /// @notice Updates the staker's staked LINK amount history /// @param staker The staker to update /// @param latestPrincipal The staker's latest staked LINK amount /// @param latestStakedAtTime The staker's latest average staked at time function _updateStakerHistory( Staker storage staker, uint256 latestPrincipal, uint256 latestStakedAtTime ) internal { staker.history.push( (uint224(uint112(latestPrincipal)) << 112) | uint224(uint112(latestStakedAtTime)) ); } /// @notice Starts the unbonding period for the staker /// @param staker The staker trying to unbond function _unbond(Staker storage staker) internal { if (staker.unbondingPeriodEndsAt != 0 && block.timestamp <= staker.claimPeriodEndsAt) { revert UnbondingOrClaimPeriodActive(staker.unbondingPeriodEndsAt); } staker.unbondingPeriodEndsAt = (block.timestamp + s_pool.configs.unbondingPeriod).toUint128(); staker.claimPeriodEndsAt = staker.unbondingPeriodEndsAt + s_pool.configs.claimPeriod; emit UnbondingPeriodStarted(msg.sender); } /// @notice Checks to see whether or not a staker is within the claim period /// to unstake their staked LINK /// @param staker The staker trying to unstake their staked LINK /// @return bool True if the staker is inside the claim period function _inClaimPeriod(Staker storage staker) private view returns (bool) { if (staker.unbondingPeriodEndsAt == 0 || block.timestamp < staker.unbondingPeriodEndsAt) { return false; } return block.timestamp <= staker.claimPeriodEndsAt; } /// @notice Util function for setting the claim period /// @param claimPeriod The claim period function _setClaimPeriod(uint256 claimPeriod) private { if (claimPeriod < i_minClaimPeriod || claimPeriod > i_maxClaimPeriod) { revert InvalidClaimPeriod(); } if (s_pool.configs.claimPeriod == claimPeriod) return; uint256 oldClaimPeriod = s_pool.configs.claimPeriod; s_pool.configs.claimPeriod = claimPeriod.toUint32(); emit ClaimPeriodSet(oldClaimPeriod, claimPeriod); } /// @notice Util function to check if the reward vault connected to this pool has rewards added to /// it /// @return bool True if the reward vault has rewards added to it, false otherwise function _hasRewardVaultRewardAdded() internal view virtual returns (bool) { return s_rewardVault.hasRewardAdded(); } /// @notice Util function to check if the pool is active /// @return bool True if the pool is active, false otherwise function _isActive() internal view returns (bool) { return s_isOpen && !s_rewardVault.hasRewardDurationEnded(address(this)); } // ========= // Modifiers // ========= /// @dev Reverts if not sent from the LINK token modifier validateFromLINK() { if (msg.sender != address(i_LINK)) revert SenderNotLinkToken(); _; } /// @dev Reverts if migration proxy is not set modifier validateMigrationProxySet() { if (s_migrationProxy == address(0)) revert MigrationProxyNotSet(); _; } /// @dev Reverts if reward vault is not set modifier validateRewardVaultSet() { if (address(s_rewardVault) == address(0)) revert RewardVaultNotSet(); _; } /// @dev Reverts if pool is after an opening modifier whenBeforeOpening() { if (s_isOpen) revert PoolHasBeenOpened(); if (s_pool.state.closedAt != 0) revert PoolHasBeenClosed(); _; } /// @dev Reverts if the pool is already closed modifier whenBeforeClosing() { if (s_pool.state.closedAt != 0) revert PoolHasBeenClosed(); _; } /// @dev Reverts if pool is not open modifier whenOpen() { if (!s_isOpen) revert PoolNotOpen(); _; } /// @dev Reverts if pool is not active (is open and rewards are available for this pool) modifier whenActive() { if (!_isActive()) revert PoolNotActive(); _; } /// @dev Reverts if pool is not closed modifier whenClosed() { if (s_pool.state.closedAt == 0) revert PoolNotClosed(); _; } /// @dev Reverts if reward vault is not open or is paused modifier whenRewardVaultOpen() { if (!s_rewardVault.isOpen() || s_rewardVault.isPaused()) revert RewardVaultNotActive(); _; } /// @dev Reverts if reward vault has not had rewards added to it modifier whenRewardVaultHasRewards() { if (!_hasRewardVaultRewardAdded()) revert RewardVaultHasNoRewards(); _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; interface ISlashable { /// @notice This error is thrown when the slasher config is invalid error InvalidSlasherConfig(); /// @notice This error is thrown when the contract manager tries to set the slasher role directly /// through /// `grantRole` error InvalidRole(); /// @notice This error is thrown then the contract manager tries to set the slasher config for an /// address /// that doesn't have the slasher role error InvalidSlasher(); /// @notice This struct defines the parameters of the slasher config struct SlasherConfig { /// @notice The pool's refill rate (Juels/sec) uint256 refillRate; /// @notice The refillable slash capacity amount uint256 slashCapacity; } /// @notice This struct defines the parameters of the slasher state struct SlasherState { /// @notice The last slash timestamp, will be 0 if never slashed /// The timestamp will be set to the time the slashing configuration was configured /// instead of 0 if slashing never occurs, refilling slash capacity to full. uint256 lastSlashTimestamp; /// @notice The current amount of remaining slash capacity uint256 remainingSlashCapacityAmount; } /// @notice This struct defines the slasher's state and config struct Slasher { /// @notice The slasher's config SlasherConfig config; /// @notice The slasher's state SlasherState state; } /// @notice Adds a new slasher with the given config /// @param slasher The address of the slasher /// @param config The slasher config function addSlasher(address slasher, SlasherConfig calldata config) external; /// @notice Removes a slasher by revoking the SLASHER_ROLE and resetting the slasher config /// @param slasher The address of the slasher function removeSlasher(address slasher) external; /// @notice Sets the slasher config /// @param slasher The address of the slasher /// @param config The slasher config function setSlasherConfig(address slasher, SlasherConfig calldata config) external; /// @notice Returns the slasher config /// @param slasher The slasher /// @return The slasher config function getSlasherConfig(address slasher) external view returns (SlasherConfig memory); /// @notice Returns the slash capacity for a slasher /// @param slasher The slasher /// @return The slash capacity function getSlashCapacity(address slasher) external view returns (uint256); /// @notice Slashes stakers and rewards the alerter. Moves slashed staker /// funds into the alerter reward funds. The alerter is then /// rewarded by the funds in the alerter reward funds. /// @param stakers The list of stakers to slash /// @param alerter The alerter that successfully raised the alert /// @param principalAmount The amount of the staker's staked LINK amount to slash /// @param alerterRewardAmount The reward amount to be given to the alerter function slashAndReward( address[] calldata stakers, address alerter, uint256 principalAmount, uint256 alerterRewardAmount ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControl.sol"; import "../utils/Context.sol"; import "../utils/Strings.sol"; import "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `_msgSender()` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. * * Format of the revert message is described in {_checkRole}. * * _Available since v4.6._ */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(account), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * May emit a {RoleGranted} event. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/IAccessControlDefaultAdminRules.sol) pragma solidity ^0.8.0; import "./IAccessControl.sol"; /** * @dev External interface of AccessControlDefaultAdminRules declared to support ERC165 detection. * * _Available since v4.9._ */ interface IAccessControlDefaultAdminRules is IAccessControl { /** * @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next * address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule` * passes. */ event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule); /** * @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule. */ event DefaultAdminTransferCanceled(); /** * @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next * delay to be applied between default admin transfer after `effectSchedule` has passed. */ event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule); /** * @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass. */ event DefaultAdminDelayChangeCanceled(); /** * @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder. */ function defaultAdmin() external view returns (address); /** * @dev Returns a tuple of a `newAdmin` and an accept schedule. * * After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role * by calling {acceptDefaultAdminTransfer}, completing the role transfer. * * A zero value only in `acceptSchedule` indicates no pending admin transfer. * * NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced. */ function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule); /** * @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started. * * This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set * the acceptance schedule. * * NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this * function returns the new delay. See {changeDefaultAdminDelay}. */ function defaultAdminDelay() external view returns (uint48); /** * @dev Returns a tuple of `newDelay` and an effect schedule. * * After the `schedule` passes, the `newDelay` will get into effect immediately for every * new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}. * * A zero value only in `effectSchedule` indicates no pending delay change. * * NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay} * will be zero after the effect schedule. */ function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule); /** * @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance * after the current timestamp plus a {defaultAdminDelay}. * * Requirements: * * - Only can be called by the current {defaultAdmin}. * * Emits a DefaultAdminRoleChangeStarted event. */ function beginDefaultAdminTransfer(address newAdmin) external; /** * @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}. * * A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function. * * Requirements: * * - Only can be called by the current {defaultAdmin}. * * May emit a DefaultAdminTransferCanceled event. */ function cancelDefaultAdminTransfer() external; /** * @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}. * * After calling the function: * * - `DEFAULT_ADMIN_ROLE` should be granted to the caller. * - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder. * - {pendingDefaultAdmin} should be reset to zero values. * * Requirements: * * - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`. * - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed. */ function acceptDefaultAdminTransfer() external; /** * @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting * into effect after the current timestamp plus a {defaultAdminDelay}. * * This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this * method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay} * set before calling. * * The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then * calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin} * complete transfer (including acceptance). * * The schedule is designed for two scenarios: * * - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by * {defaultAdminDelayIncreaseWait}. * - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`. * * A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change. * * Requirements: * * - Only can be called by the current {defaultAdmin}. * * Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event. */ function changeDefaultAdminDelay(uint48 newDelay) external; /** * @dev Cancels a scheduled {defaultAdminDelay} change. * * Requirements: * * - Only can be called by the current {defaultAdmin}. * * May emit a DefaultAdminDelayChangeCanceled event. */ function rollbackDefaultAdminDelay() external; /** * @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay}) * to take effect. Default to 5 days. * * When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with * the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) * that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can * be overrode for a custom {defaultAdminDelay} increase scheduling. * * IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise, * there's a risk of setting a high new delay that goes into effect almost immediately without the * possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds). */ function defaultAdminDelayIncreaseWait() external view returns (uint48); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5313.sol) pragma solidity ^0.8.0; /** * @dev Interface for the Light Contract Ownership Standard. * * A standardized minimal interface required to identify an account that controls a contract * * _Available since v4.9._ */ interface IERC5313 { /** * @dev Gets the address of the owner. */ function owner() external view returns (address); }
// 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.6; interface ERC677ReceiverInterface { function onTokenTransfer( address sender, uint256 amount, bytes calldata data ) external; }
// 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.19; interface IMigratable { /// @notice This error is thrown when the owner tries to set the migration target to the /// zero address or an invalid address as well as when the migration target is not set and owner /// tries to migrate the contract. error InvalidMigrationTarget(); /// @notice This event is emitted when the migration target is set /// @param oldMigrationTarget The previous migration target /// @param newMigrationTarget The updated migration target event MigrationTargetSet(address indexed oldMigrationTarget, address indexed newMigrationTarget); /// @notice Sets the address this contract will be upgraded to /// @param newMigrationTarget The address of the migration target function setMigrationTarget(address newMigrationTarget) external; /// @notice Returns the current migration target of the contract /// @return address The current migration target function getMigrationTarget() external view returns (address); /// @notice Migrates the contract /// @param data Optional calldata to call on new contract function migrate(bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; interface IStakingOwner { /// @notice This event is emitted when the staking pool is opened for staking event PoolOpened(); /// @notice This event is emitted when the staking pool is closed event PoolClosed(); /// @notice This error is thrown when an invalid min operator stake amount is /// supplied error InvalidMinStakeAmount(); /// @notice This error is raised when attempting to decrease maximum pool size /// @param maxPoolSize the proposed maximum pool size error InvalidPoolSize(uint256 maxPoolSize); /// @notice This error is raised when attempting to decrease maximum stake amount /// for the pool members /// @param maxStakeAmount the proposed maximum stake amount error InvalidMaxStakeAmount(uint256 maxStakeAmount); /// @notice This error is thrown when the staking pool is closed. error PoolNotOpen(); /// @notice This error is thrown when the staking pool is open. error PoolNotClosed(); /// @notice This error is thrown when the staking pool has been opened and contract manager tries /// to re-open. error PoolHasBeenOpened(); /// @notice This error is thrown when the pool has been closed and contract manager tries to /// re-open error PoolHasBeenClosed(); /// @notice Set the pool config /// @param maxPoolSize The max amount of staked LINK allowed in the pool /// @param maxPrincipalPerStaker The max amount of LINK a staker can stake /// in the pool. function setPoolConfig(uint256 maxPoolSize, uint256 maxPrincipalPerStaker) external; /// @notice Opens the pool for staking function open() external; /// @notice Closes the pool function close() external; /// @notice Sets the migration proxy contract address /// @param migrationProxy The migration proxy contract address function setMigrationProxy(address migrationProxy) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {AccessControlDefaultAdminRules} from "@openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol"; import {IMigratable} from "./interfaces/IMigratable.sol"; /// @notice Base contract that adds migration functionality. abstract contract Migratable is IMigratable, AccessControlDefaultAdminRules { /// @notice The address of the new contract that this contract will be upgraded to. address internal s_migrationTarget; function setMigrationTarget(address newMigrationTarget) external override onlyRole(DEFAULT_ADMIN_ROLE) { _validateMigrationTarget(newMigrationTarget); address oldMigrationTarget = s_migrationTarget; s_migrationTarget = newMigrationTarget; emit MigrationTargetSet(oldMigrationTarget, newMigrationTarget); } /// @inheritdoc IMigratable function getMigrationTarget() external view returns (address) { return s_migrationTarget; } /// @notice Helper function for validating the migration target /// @param newMigrationTarget The address of the new migration target function _validateMigrationTarget(address newMigrationTarget) internal virtual { if ( newMigrationTarget == address(0) || newMigrationTarget == address(this) || newMigrationTarget == s_migrationTarget || newMigrationTarget.code.length == 0 ) { revert InvalidMigrationTarget(); } } /// @dev Reverts if the migration target is not set modifier validateMigrationTargetSet() { if (s_migrationTarget == address(0)) { revert InvalidMigrationTarget(); } _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
{ "remappings": [ "@chainlink/=lib/chainlink/", "@openzeppelin/=lib/openzeppelin-contracts/", "@solmate/=lib/solmate/src/", "chainlink/=lib/chainlink/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/", "lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/src/", "lib/openzeppelin-contracts:ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/", "lib/openzeppelin-contracts:erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "lib/openzeppelin-contracts:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/", "lib/openzeppelin-contracts:openzeppelin/=lib/openzeppelin-contracts/contracts/", "lib/solmate:ds-test/=lib/solmate/lib/ds-test/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"components":[{"internalType":"contract LinkTokenInterface","name":"linkToken","type":"address"},{"internalType":"contract CommunityStakingPool","name":"communityStakingPool","type":"address"},{"internalType":"contract OperatorStakingPool","name":"operatorStakingPool","type":"address"},{"internalType":"uint32","name":"delegationRate","type":"uint32"},{"internalType":"uint32","name":"multiplierDuration","type":"uint32"},{"internalType":"uint48","name":"adminRoleTransferDelay","type":"uint48"}],"internalType":"struct RewardVault.ConstructorParams","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessForbidden","type":"error"},{"inputs":[],"name":"InsufficentRewardsForDelegationRate","type":"error"},{"inputs":[],"name":"InvalidDelegationRate","type":"error"},{"inputs":[],"name":"InvalidEmissionRate","type":"error"},{"inputs":[],"name":"InvalidPool","type":"error"},{"inputs":[],"name":"InvalidRewardAmount","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"NoRewardToClaim","type":"error"},{"inputs":[],"name":"RewardDurationTooShort","type":"error"},{"inputs":[],"name":"VaultAlreadyClosed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"baseRewardPerToken","type":"uint256"}],"name":"CommunityPoolRewardUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"DefaultAdminDelayChangeCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"newDelay","type":"uint48"},{"indexed":false,"internalType":"uint48","name":"effectSchedule","type":"uint48"}],"name":"DefaultAdminDelayChangeScheduled","type":"event"},{"anonymous":false,"inputs":[],"name":"DefaultAdminTransferCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"},{"indexed":false,"internalType":"uint48","name":"acceptSchedule","type":"uint48"}],"name":"DefaultAdminTransferScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldDelegationRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDelegationRate","type":"uint256"}],"name":"DelegationRateSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"vestedReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vestedRewardPerToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reclaimedReward","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isOperatorReward","type":"bool"}],"name":"ForfeitedRewardDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"baseRewardPerToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegatedRewardPerToken","type":"uint256"}],"name":"OperatorPoolRewardUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"emissionRate","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimedRewards","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"bool","name":"shouldForfeit","type":"bool"}],"name":"RewardFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"vestedBaseReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vestedDelegatedReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseRewardPerToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"operatorDelegatedRewardPerToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimedBaseRewardsInPeriod","type":"uint256"}],"name":"StakerRewardUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalUnvestedRewards","type":"uint256"}],"name":"VaultClosed","type":"event"},{"anonymous":false,"inputs":[],"name":"VaultOpened","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"emissionRate","type":"uint256"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"beginDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"calculateLatestStakerReward","outputs":[{"components":[{"internalType":"uint112","name":"vestedBaseReward","type":"uint112"},{"internalType":"uint112","name":"vestedDelegatedReward","type":"uint112"},{"internalType":"uint112","name":"baseRewardPerToken","type":"uint112"},{"internalType":"uint112","name":"operatorDelegatedRewardPerToken","type":"uint112"},{"internalType":"enum IRewardVault.StakerType","name":"stakerType","type":"uint8"},{"internalType":"uint112","name":"claimedBaseRewardsInPeriod","type":"uint112"},{"internalType":"uint112","name":"unvestedBaseReward","type":"uint112"}],"internalType":"struct IRewardVault.StakerReward","name":"","type":"tuple"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"}],"name":"changeDefaultAdminDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"close","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"oldPrincipal","type":"uint256"},{"internalType":"uint256","name":"stakedAt","type":"uint256"},{"internalType":"uint256","name":"unstakedAmount","type":"uint256"},{"internalType":"bool","name":"shouldForfeit","type":"bool"}],"name":"concludeRewardPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAdminDelay","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAdminDelayIncreaseWait","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyUnpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDelegationRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFinalVestingCheckpointData","outputs":[{"components":[{"internalType":"uint256","name":"operatorPoolTotalPrincipal","type":"uint256"},{"internalType":"uint256","name":"communityPoolTotalPrincipal","type":"uint256"},{"internalType":"uint256","name":"finalBlockNumber","type":"uint256"}],"internalType":"struct RewardVault.VestingCheckpointData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMultiplierDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardBuckets","outputs":[{"components":[{"components":[{"internalType":"uint80","name":"emissionRate","type":"uint80"},{"internalType":"uint80","name":"rewardDurationEndsAt","type":"uint80"},{"internalType":"uint80","name":"vestedRewardPerToken","type":"uint80"}],"internalType":"struct RewardVault.RewardBucket","name":"operatorBase","type":"tuple"},{"components":[{"internalType":"uint80","name":"emissionRate","type":"uint80"},{"internalType":"uint80","name":"rewardDurationEndsAt","type":"uint80"},{"internalType":"uint80","name":"vestedRewardPerToken","type":"uint80"}],"internalType":"struct RewardVault.RewardBucket","name":"communityBase","type":"tuple"},{"components":[{"internalType":"uint80","name":"emissionRate","type":"uint80"},{"internalType":"uint80","name":"rewardDurationEndsAt","type":"uint80"},{"internalType":"uint80","name":"vestedRewardPerToken","type":"uint80"}],"internalType":"struct RewardVault.RewardBucket","name":"operatorDelegated","type":"tuple"}],"internalType":"struct RewardVault.RewardBuckets","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardPerTokenUpdatedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakingPools","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getStoredReward","outputs":[{"components":[{"internalType":"uint112","name":"vestedBaseReward","type":"uint112"},{"internalType":"uint112","name":"vestedDelegatedReward","type":"uint112"},{"internalType":"uint112","name":"baseRewardPerToken","type":"uint112"},{"internalType":"uint112","name":"operatorDelegatedRewardPerToken","type":"uint112"},{"internalType":"enum IRewardVault.StakerType","name":"stakerType","type":"uint8"},{"internalType":"uint112","name":"claimedBaseRewardsInPeriod","type":"uint112"},{"internalType":"uint112","name":"unvestedBaseReward","type":"uint112"}],"internalType":"struct IRewardVault.StakerReward","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnvestedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"hasRewardAdded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stakingPool","type":"address"}],"name":"hasRewardDurationEnded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOpen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingDefaultAdmin","outputs":[{"internalType":"address","name":"newAdmin","type":"address"},{"internalType":"uint48","name":"schedule","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingDefaultAdminDelay","outputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"},{"internalType":"uint48","name":"schedule","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollbackDefaultAdminDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newDelegationRate","type":"uint256"}],"name":"setDelegationRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"stakerPrincipal","type":"uint256"}],"name":"updateReward","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101006040523480156200001257600080fd5b5060405162005108380380620051088339810160408190526200003591620003ce565b60a08101516000805460ff19169055338181806200009a5760405162461bcd60e51b815260206004820152601e60248201527f416363657373436f6e74726f6c3a20302064656661756c742061646d696e000060448201526064015b60405180910390fd5b600280546001600160d01b0316600160d01b65ffffffffffff851602179055620000c660008262000249565b505082516001600160a01b03169150620000f590505760405163f6b2911f60e01b815260040160405180910390fd5b60208101516001600160a01b0316620001215760405163f6b2911f60e01b815260040160405180910390fd5b60408101516001600160a01b03166200014d5760405163f6b2911f60e01b815260040160405180910390fd5b612710816060015163ffffffff1611156200017b5760405163027953ef60e61b815260040160405180910390fd5b60808181015163ffffffff90811690915281516001600160a01b0390811660a052602080840151821660c05260408085015190921660e05260608401516007805463ffffffff191691909416908117909355815160008152908101929092527f03402a75be6462f03cfaa1a1d189e17ecf71c8b57bf127e93f5f5aeae974b43c910160405180910390a16007805460ff60201b19166401000000001790556040517f539851b0675b473b0130d32676207eae7491ebd4ed2d8aba5c394b6c5c4c2c8190600090a15062000488565b81620002ed576000620002646003546001600160a01b031690565b6001600160a01b031614620002d15760405162461bcd60e51b815260206004820152602c60248201527f416363657373436f6e74726f6c3a2064656661756c742061646d696e20616c7260448201526b1958591e4819dc985b9d195960a21b606482015260840162000091565b600380546001600160a01b0319166001600160a01b0383161790555b620002f98282620002fd565b5050565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff16620002f95760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b80516001600160a01b03811681146200039d57600080fd5b919050565b805163ffffffff811681146200039d57600080fd5b805165ffffffffffff811681146200039d57600080fd5b600060c08284031215620003e157600080fd5b60405160c081016001600160401b03811182821017156200041257634e487b7160e01b600052604160045260246000fd5b604052620004208362000385565b8152620004306020840162000385565b6020820152620004436040840162000385565b60408201526200045660608401620003a2565b60608201526200046960808401620003a2565b60808201526200047c60a08401620003b7565b60a08201529392505050565b60805160a05160c05160e051614b2e620005da600039600081816106b40152818161074201528181610b4301528181610dd001528181610f97015281816111c0015281816112d30152818161136a015281816116ba0152818161183b015281816118bb015281816119be015281816128820152818161291401528181612bf101528181612ecf01528181613712015281816139e801528181613b3901528181613c3f01528181613cd701526141b00152600081816106e801528181610b9701528181610d920152818161100b0152818161119a015281816112ad01528181611344015281816116940152818161186f0152818161199801528181612bcb01528181612df4015281816136e501528181613a7301528181613b7901528181613bff0152613d1b015260008181610c5501528181610e78015261155a0152600081816104720152612d1b0152614b2e6000f3fe608060405234801561001057600080fd5b506004361061027f5760003560e01c806370a205b81161015c578063c00007b0116100ce578063d602b9fd11610087578063d602b9fd146105df578063de0b985a146105e7578063e19e3d7f146105fc578063e63ab1e91461060f578063fcb6813e14610636578063ffafc0b11461063e57600080fd5b8063c00007b014610568578063cc8463c81461057b578063cefc142914610583578063cf3bf2eb1461058b578063cf6eefb71461059e578063d547741f146105cc57600080fd5b806391d148541161012057806391d148541461050b578063a1eda53c1461051e578063a217fddf14610545578063a9d637e11461054d578063b187bd2614610402578063b88a802f1461056057600080fd5b806370a205b81461047057806384ef8ffc146104965780638580cf76146104bb57806387621cbb146104e25780638da5cb5b1461050357600080fd5b806343d726d6116101f55780635350e731116101b95780635350e731146103ef5780635c975abb146104025780635e2746541461040d578063634e93da1461042d578063649a5ec71461044057806365118b3f1461045357600080fd5b806343d726d6146103b257806344ea559a146103ba57806347535d7b146103cd5780634a4e3bd5146103df57806351858e27146103e757600080fd5b80631a399125116102475780631a3991251461031d578063248a9ca3146103305780632f2ff15d1461035457806336568abe146103675780633c70a5841461037a578063417243c91461039d57600080fd5b806301ffc9a714610284578063022d63fb146102ac5780630aa6220b146102c85780630c7e5c23146102d2578063181f5a77146102ea575b600080fd5b61029761029236600461455c565b610668565b60405190151581526020015b60405180910390f35b620697805b60405165ffffffffffff90911681526020016102a3565b6102d0610693565b005b60075463ffffffff165b6040519081526020016102a3565b604080518082018252601181527005265776172645661756c7420312e302e3607c1b602082015290516102a391906145aa565b6102d061032b3660046145f9565b6106a9565b6102dc61033e366004614623565b6000908152600160208190526040909120015490565b6102d061036236600461463c565b6108ce565b6102d061037536600461463c565b61094d565b610382610a37565b604080519384526020840192909252908201526060016102a3565b6103a5610b1c565b6040516102a39190614668565b6102d0610be6565b6102d06103c83660046146b5565b610d04565b600754600160201b900460ff16610297565b6102d0610f2f565b6102d0610f61565b6102976103fd3660046146e8565b610f93565b60005460ff16610297565b61042061041b3660046146e8565b611076565b6040516102a391906147ab565b6102d061043b3660046146e8565b61113d565b6102d061044e3660046147b9565b611151565b61045b611165565b604080519283526020830191909152016102a3565b7f00000000000000000000000000000000000000000000000000000000000000006102dc565b6003546001600160a01b03165b6040516001600160a01b0390911681526020016102a3565b6102dc7fbeec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f681565b6104f56104f03660046146e8565b61117b565b6040516102a39291906147e1565b6104a3611207565b61029761051936600461463c565b611220565b61052661124b565b6040805165ffffffffffff9384168152929091166020830152016102a3565b6102dc600081565b6102dc61055b3660046146e8565b61129b565b6102dc61130e565b6102dc6105763660046146e8565b61167d565b6102b1611753565b6102d06117b2565b6102d061059936600461480b565b611830565b6105a6611bd1565b604080516001600160a01b03909316835265ffffffffffff9091166020830152016102a3565b6102d06105da36600461463c565b611bf2565b6102d0611c6f565b6105ef611c82565b6040516102a3919061485f565b6102d061060a366004614623565b611d88565b6102dc7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610297612055565b610646612093565b60408051825181526020808401519082015291810151908201526060016102a3565b60006001600160e01b031982166318a4c3c360e11b148061068d575061068d826120dc565b92915050565b600061069e81612111565b6106a661211b565b50565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480159061070b5750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b1561072957604051631decfebb60e31b815260040160405180910390fd5b610731612128565b600061076983336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148461227d565b6001600160a01b0384166000908152600c602090815260409182902083518154928501516001600160701b039182166001600160e01b031994851617600160701b918316820217835593850151600183018054606088015192841695169490941791169093029290921780825560808401519394508493919060ff60e01b1916600160e01b83600281111561080057610800614703565b021790555060a0828101516002909201805460c0909401516001600160701b03908116600160701b026001600160e01b03199095169316929092179290921790558151602083015160408085015160608601519486015191516001600160a01b038916957f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a956108c19590949093926001600160701b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b60405180910390a2505050565b8161093f5760405162461bcd60e51b815260206004820152603660248201527f416363657373436f6e74726f6c3a2063616e2774206469726563746c79206772604482015275616e742064656661756c742061646d696e20726f6c6560501b60648201526084015b60405180910390fd5b610949828261245a565b5050565b8115801561096857506003546001600160a01b038281169116145b15610a2d57600080610978611bd1565b90925090506001600160a01b03821615801561099b575065ffffffffffff811615155b80156109ae57504265ffffffffffff8216105b610a185760405162461bcd60e51b815260206004820152603560248201527f416363657373436f6e74726f6c3a206f6e6c792063616e2072656e6f756e636560448201527420696e2074776f2064656c6179656420737465707360581b6064820152608401610936565b50506002805465ffffffffffff60a01b191690555b6109498282612485565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090819081908190610a81906124ff565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150600090610ac8906124ff565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150600090610b0f906124ff565b9296919550919350915050565b604080516002808252606080830184529260009291906020830190803683370190505090507f000000000000000000000000000000000000000000000000000000000000000081600081518110610b7557610b756148f1565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000000000000000000000000000000000000000000081600181518110610bc957610bc96148f1565b6001600160a01b0390921660209283029190910190910152919050565b6000610bf181612111565b600754600160201b900460ff16610c1b5760405163fc5c1c4560e01b815260040160405180910390fd5b6000610c25612551565b50506007805464ff00000000191690555060405163a9059cbb60e01b8152336004820152602481018290529092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316915063a9059cbb906044016020604051808303816000875af1158015610ca7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccb9190614907565b506040518181527f4ef41257ba66fe15e89bff4221410f2c2a555d82f884ca0b3b29898d28635a8f906020015b60405180910390a15050565b610d2e7fbeec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f633611220565b610d4b57604051631decfebb60e31b815260040160405180910390fd5b600754600160201b900460ff16610d755760405163fc5c1c4560e01b815260040160405180910390fd5b610d7d6125f1565b6001600160a01b03831615801590610dc757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614155b8015610e0557507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614155b15610e225760405162820f3560e61b815260040160405180910390fd5b80600003610e435760405163041c928b60e11b815260040160405180910390fd5b610e4b612128565b610e56838383612637565b6040516323b872dd60e01b8152336004820152306024820152604481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610ec9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eed9190614907565b5060408051838152602081018390526001600160a01b038516917f6a6f77044107a33658235d41bedbbaf2fe9ccdceb313143c947a5e76e1ec847491016108c1565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f5981612111565b6106a66126bd565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f8b81612111565b6106a661270f565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036110095760045442600160501b9091046001600160501b03161180159061068d57505060065442600160501b9091046001600160501b03161115919050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361105e57505060055442600160501b9091046001600160501b0316111590565b60405162820f3560e61b815260040160405180910390fd5b61107e61451f565b6001600160a01b0382166000908152600c6020908152604091829020825160e08101845281546001600160701b038082168352600160701b918290048116948301949094526001830154808516958301959095528404909216606083015290916080830190600160e01b900460ff1660028111156110fe576110fe614703565b600281111561110f5761110f614703565b8152600291909101546001600160701b038082166020840152600160701b9091041660409091015292915050565b600061114881612111565b6109498261274c565b600061115c81612111565b610949826127bf565b600080611173600b54612828565b915091509091565b61118361451f565b60008061118f84612860565b90506000816111be577f00000000000000000000000000000000000000000000000000000000000000006111e0565b7f00000000000000000000000000000000000000000000000000000000000000005b905060006111ee868361297f565b90506111fb868285612a7a565b94509450505050915091565b600061121b6003546001600160a01b031690565b905090565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600354600090600160d01b900465ffffffffffff16801515801561127757504265ffffffffffff821610155b61128357600080611173565b600354600160a01b900465ffffffffffff1692909150565b6000806112a783612860565b6112d1577f00000000000000000000000000000000000000000000000000000000000000006112f3565b7f00000000000000000000000000000000000000000000000000000000000000005b90506113076113028483612c6b565b612ce7565b9392505050565b60006113186125f1565b600061132333612860565b905061133b81611334576001612d77565b6002612d77565b600081611368577f000000000000000000000000000000000000000000000000000000000000000061138a565b7f00000000000000000000000000000000000000000000000000000000000000005b90506000611398338361297f565b905060006113a733858461227d565b905060006113c1826113bc6113023388612c6b565b613035565b9050808260c0018181516113d5919061493a565b6001600160701b031690525060a0820180518291906113f590839061495a565b6001600160701b0316905250815160009061141190839061495a565b600084526001600160701b03169050851561144857602083015161143e906001600160701b03168261497a565b6000602085015290505b8060000361146957604051635aa9184d60e01b815260040160405180910390fd5b336000908152600c602090815260409182902085518154928701516001600160701b039182166001600160e01b031994851617600160701b91831682021783559387015160018301805460608a01519284169516949094179116909302929092178082556080860151869392909160ff60e01b1916600160e01b8360028111156114f5576114f5614703565b021790555060a0820151600291909101805460c0909301516001600160701b03908116600160701b026001600160e01b031990941692169190911791909117905560405163a9059cbb60e01b8152336004820152602481018290526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156115a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115c79190614907565b5060405181815233907f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f72419060200160405180910390a2604080840151606085015160a0860151925133937f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a9361166b9360009384939094855260208501939093526001600160701b0391821660408501528116606084015216608082015260a00190565b60405180910390a29550505050505090565b60008061168983612860565b90506000816116b8577f00000000000000000000000000000000000000000000000000000000000000006116da565b7f00000000000000000000000000000000000000000000000000000000000000005b905060006116e8858361297f565b90506000806116f8878487612a7a565b91509150600061171b828561170c88613070565b611716919061498d565b6130ef565b925050508083602001518460000151611734919061495a565b6001600160701b0316611747919061497a565b98975050505050505050565b600354600090600160d01b900465ffffffffffff16801515801561177e57504265ffffffffffff8216105b61179957600254600160d01b900465ffffffffffff166117ac565b600354600160a01b900465ffffffffffff165b91505090565b60006117bc611bd1565b509050336001600160a01b038216146118285760405162461bcd60e51b815260206004820152602860248201527f416363657373436f6e74726f6c3a2070656e64696e672061646d696e206d75736044820152671d081858d8d95c1d60c21b6064820152608401610936565b6106a661313a565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015906118925750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b156118b057604051631decfebb60e31b815260040160405180910390fd5b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146118eb81611334576001612d77565b60006118f887838861227d565b90506000611909826113bc88612ce7565b9050808260c00181815161191d919061493a565b6001600160701b031690525081518190839061193a90839061495a565b6001600160701b0316905250600060a0830152836119655761195d888386613205565b505050611bca565b60c08201516001600160701b03811660000361198f57611986898487613205565b50505050611bca565b6000846119bc577f00000000000000000000000000000000000000000000000000000000000000006119de565b7f00000000000000000000000000000000000000000000000000000000000000005b90506000896119ec83613070565b6119f6919061498d565b905080600003611a8757600060c0860152845183908690611a1890839061495a565b6001600160701b0390811690915260408051600080825260208201529186169082015287151560608201527feb34a4cfa3869c57492c653f9c06f128abac61952688567d4396e9ffaf93a67a915060800160405180910390a1611a7c8b8689613205565b505050505050611bca565b60008a15611ab2578a611aa38a6001600160701b0387166149a0565b611aad91906149cd565b611abd565b836001600160701b03165b9050600087611acd576005611ad0565b60045b90506000611ade83856133a3565b9050611ae9816133b8565b82548390601490611b0b908490600160a01b90046001600160501b03166149e1565b92506101000a8154816001600160501b0302191690836001600160501b031602179055507feb34a4cfa3869c57492c653f9c06f128abac61952688567d4396e9ffaf93a67a838260008c604051611b7d9493929190938452602084019290925260408301521515606082015260800190565b60405180910390a1611b8f888a613424565b611b9883613473565b8860c001818151611ba9919061493a565b6001600160701b0316905250611bc08e898c613205565b5050505050505050505b5050505050565b6002546001600160a01b03811691600160a01b90910465ffffffffffff1690565b81611c655760405162461bcd60e51b815260206004820152603760248201527f416363657373436f6e74726f6c3a2063616e2774206469726563746c7920726560448201527f766f6b652064656661756c742061646d696e20726f6c650000000000000000006064820152608401610936565b61094982826134dc565b6000611c7a81612111565b6106a6613502565b611ced6040805160c081019091526000606082018181526080830182905260a083019190915281908152604080516060810182526000808252602082810182905292820152910190815260408051606081018252600080825260208281018290529282015291015290565b506040805160c0810182526004546001600160501b038082166060808501918252600160501b80850484166080870152600160a01b94859004841660a08701529185528551808201875260055480851682528381048516602083810191909152908690048516828901528087019190915286519182018752600654808516835292830484169082015292900416818401529181019190915290565b6000611d9381612111565b60075463ffffffff1682811480611dab575061271083115b15611dc95760405163027953ef60e61b815260040160405180910390fd5b600654600554600091611de8916001600160501b0391821691166149e1565b6001600160501b0316905060008415611e1757612710611e0886846149a0565b611e1291906149cd565b611e1a565b60005b905080158015611e2957508415155b8015611e3457508115155b15611e525760405163ba00abbd60e01b815260040160405180910390fd5b611e5a612128565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090611e9e906124ff565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152611edf906124ff565b611ee9919061497a565b90506000611ef7838561498d565b9050611f02816133b8565b6005805469ffffffffffffffffffff19166001600160501b0392909216919091179055611f2e836133b8565b6006805469ffffffffffffffffffff19166001600160501b03929092169190911790556000879003611f7e5760068054600160501b600160a01b0319169055611f796005838361350d565b611ff0565b6127108703611fa65760058054600160501b600160a01b0319169055611f796006838561350d565b8115611ff0576000612710611fbb89856149a0565b611fc591906149cd565b90506000611fd3828561498d565b9050611fe16005828561350d565b611fed6006838761350d565b50505b611ff98761355c565b6007805463ffffffff191663ffffffff9290921691909117905560408051868152602081018990527f03402a75be6462f03cfaa1a1d189e17ecf71c8b57bf127e93f5f5aeae974b43c910160405180910390a150505050505050565b6004546000906001600160501b031615158061207b57506005546001600160501b031615155b8061121b5750506006546001600160501b0316151590565b6120b760405180606001604052806000815260200160008152602001600081525090565b506040805160608101825260085481526009546020820152600a549181019190915290565b60006001600160e01b03198216637965db0b60e01b148061068d57506301ffc9a760e01b6001600160e01b031983161461068d565b6106a681336135c1565b61212660008061361a565b565b600080612136600b54612828565b91509150428214801561214857504281145b15612151575050565b600080600061215e6136da565b92509250925061216d836133b8565b600580546001600160501b0392909216600160a01b02600160a01b600160f01b03199092169190911790556121a1826133b8565b600480546001600160501b0392909216600160a01b02600160a01b600160f01b03199092169190911790556121d5816133b8565b600680546001600160501b0392909216600160a01b02600160a01b600160f01b031990921691909117905561220a4280613823565b6040518381527f1236d5f7290187e9ac80880b3e28a193a9f38bb8bdc8a8a197744d80c50ca3269060200160405180910390a160408051838152602081018390527f9eecdebdae54d18e0c665dae22811251367c65647ec09aa5e33f001a5c147d22910160405180910390a15050505050565b61228561451f565b6001600160a01b0384166000908152600c60209081526040808320815160e08101835281546001600160701b038082168352600160701b9182900481169583019590955260018301548086169483019490945283049093166060840152906080830190600160e01b900460ff16600281111561230357612303614703565b600281111561231457612314614703565b8152600291909101546001600160701b038082166020840152600160701b90910416604090910152905060008160800151600281111561235657612356614703565b03612397578361236757600161236a565b60025b8160800190600281111561238057612380614703565b9081600281111561239357612393614703565b9052505b6123e36123de8285876123bc57600554600160a01b90046001600160501b03166123d0565b600454600160a01b90046001600160501b03165b6001600160501b0316613847565b613473565b8160c0018181516123f4919061495a565b6001600160701b031690525083156124485760065461242a906123de9083908690600160a01b90046001600160501b031661386b565b8160200181815161243b919061495a565b6001600160701b03169052505b6124528185613424565b949350505050565b6000828152600160208190526040909120015461247681612111565b6124808383613886565b505050565b6001600160a01b03811633146124f55760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610936565b6109498282613930565b60004282602001516001600160501b03161115612549574282602001516001600160501b031661252f919061498d565b825161254491906001600160501b03166149a0565b61068d565b600092915050565b6000806000806000612561612128565b600061256d600461396b565b9050600061257b600561396b565b90506000612589600661396b565b9050600081612598848661497a565b6125a2919061497a565b90506125ac6139e6565b6006546005546004546001600160501b03928316926125ce92811691166149e1565b6125d891906149e1565b6001600160501b03169990985092965090945092509050565b60005460ff16156121265760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610936565b6007546000906126549085908590859063ffffffff161515613afc565b90508060600151600014612675578051606082015161267591600591613ebc565b60808101511561269657612696600460000182602001518360800151613ebc565b60a0810151156126b7576126b7600460020182604001518360a00151613ebc565b50505050565b6126c5613f6b565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6127176125f1565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586126f23390565b6000612756611753565b61275f42613fb4565b6127699190614a01565b9050612775828261401b565b60405165ffffffffffff821681526001600160a01b038316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b60006127ca8261409a565b6127d342613fb4565b6127dd9190614a01565b90506127e9828261361a565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9101610cf8565b6000808061283e670de0b6b3a7640000856149cd565b90506000612854670de0b6b3a764000086614a20565b91959194509092505050565b6040516336b87bd760e11b81526001600160a01b0382811660048301526000917f000000000000000000000000000000000000000000000000000000000000000090911690636d70f7ae90602401602060405180830381865afa1580156128cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ef9190614907565b8061068d5750604051637d3374e560e01b81526001600160a01b0383811660048301527f00000000000000000000000000000000000000000000000000000000000000001690637d3374e590602401602060405180830381865afa15801561295b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068d9190614907565b600754600090600160201b900460ff16612a0f57600a54604051634c5b725f60e01b81526001600160a01b038581166004830152602482019290925290831690634c5b725f906044015b602060405180830381865afa1580156129e6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a0a9190614a34565b611307565b604051630706983f60e51b81526001600160a01b03848116600483015283169063e0d307e0906024015b602060405180830381865afa158015612a56573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113079190614a34565b612a8261451f565b6001600160a01b0384166000908152600c60209081526040808320815160e08101835281546001600160701b038082168352600160701b91829004811695830195909552600183015480861694830194909452830490931660608401528392916080830190600160e01b900460ff166002811115612b0257612b02614703565b6002811115612b1357612b13614703565b8152600291909101546001600160701b038082166020840152600160701b90910416604090910152905060008080612b496136da565b925092509250612b686123de858a8a612b625786613847565b85613847565b8460c001818151612b79919061495a565b6001600160701b03169052508615612bb757612b996123de858a8461386b565b84602001818151612baa919061495a565b6001600160701b03169052505b6000612c15856113bc6113028d8c612bef577f0000000000000000000000000000000000000000000000000000000000000000612c6b565b7f0000000000000000000000000000000000000000000000000000000000000000612c6b565b90508085600001818151612c29919061495a565b6001600160701b031690525060c0850151600090612c4890839061493a565b600060c0880152959b6001600160701b039096169a509498505050505050505050565b600754600090600160201b900460ff16612cb957600a54604051634b669bff60e11b81526001600160a01b0385811660048301526024820192909252908316906396cd37fe906044016129c9565b604051631624e46160e21b81526001600160a01b038481166004830152831690635893918490602401612a39565b600081600003612cf957506000919050565b600754600160201b900460ff16612d195750670de0b6b3a7640000919050565b7f00000000000000000000000000000000000000000000000000000000000000006000819003612d535750670de0b6b3a764000092915050565b611307612d69612d63854261498d565b836133a3565b670de0b6b3a76400006140d9565b600080612d85600b54612828565b90925090506001836002811115612d9e57612d9e614703565b03612ea357428203612daf57505050565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152612e2390612e1e90612e187f0000000000000000000000000000000000000000000000000000000000000000613070565b856140ef565b6133b8565b600580546001600160501b0392909216600160a01b02600160a01b600160f01b0319909216919091179055612e584282613823565b600554604051600160a01b9091046001600160501b031681527f1236d5f7290187e9ac80880b3e28a193a9f38bb8bdc8a8a197744d80c50ca3269060200160405180910390a1505050565b6002836002811115612eb757612eb7614703565b0361248057428103612ec857505050565b6000612ef37f0000000000000000000000000000000000000000000000000000000000000000613070565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150612f3d90612e1e9083856140ef565b60048054600160a01b600160f01b031916600160a01b6001600160501b03938416810291909117909155604080516060810182526006548085168252600160501b8104851660208301529290920490921691810191909152612fa490612e1e9083856140ef565b600680546001600160501b0392909216600160a01b02600160a01b600160f01b0319909216919091179055612fd98342613823565b600454600654604080516001600160501b03600160a01b94859004811682529390920490921660208201527f9eecdebdae54d18e0c665dae22811251367c65647ec09aa5e33f001a5c147d22910160405180910390a150505050565b60008260a001516130666123de848660a001518760c00151613057919061495a565b6001600160701b031690614197565b611307919061493a565b600754600090600160201b900460ff1661308d57612544826141ac565b816001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130cb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068d9190614a34565b60008060008460000361310a57506000915081905080613133565b6000808086156131285787925061312183886133a3565b915061312b565b50865b919450925090505b9250925092565b600080613145611bd1565b9150915061315a8165ffffffffffff16151590565b801561316d57504265ffffffffffff8216105b6131ca5760405162461bcd60e51b815260206004820152602860248201527f416363657373436f6e74726f6c3a207472616e736665722064656c6179206e6f6044820152671d081c185cdcd95960c21b6064820152608401610936565b6131e660006131e16003546001600160a01b031690565b613930565b6131f1600083613886565b5050600280546001600160d01b0319169055565b6001600160a01b0383166000908152600c602090815260409182902084518154928601516001600160701b039182166001600160e01b031994851617600160701b91831682021783559386015160018301805460608901519284169516949094179116909302929092178082556080850151859392909160ff60e01b1916600160e01b83600281111561329a5761329a614703565b021790555060a08201516002909101805460c0909301516001600160701b03908116600160701b026001600160e01b03199094169216919091179190911790556040516001600160a01b038416907fa6b1f21803a1471cf6981bffc4c07ff6610f2b88efc44f62b96dca65a63d3d279061331990841515815260200190565b60405180910390a2826001600160a01b03167f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a83600001518460200151856040015186606001518760a001516040516108c19594939291906001600160701b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b600061130783670de0b6b3a7640000846141f7565b60006001600160501b038211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201526530206269747360d01b6064820152608401610936565b5090565b8015613456576004546001600160501b03600160a01b9182900481166040850152600654919091041660608301525050565b6005546001600160501b03600160a01b9091041660408301525050565b60006001600160701b038211156134205760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663132206269747360c81b6064820152608401610936565b600082815260016020819052604090912001546134f881612111565b6124808383613930565b61212660008061401b565b8060000361351a57505050565b61353161352782846149cd565b612e1e904261497a565b83546001600160501b0391909116600160501b02600160501b600160a01b0319909116179092555050565b600063ffffffff8211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610936565b6135cb8282611220565b610949576135d881614215565b6135e3836020614227565b6040516020016135f4929190614a4d565b60408051601f198184030181529082905262461bcd60e51b8252610936916004016145aa565b600354600160d01b900465ffffffffffff16801561369d574265ffffffffffff8216101561367357600354600280546001600160d01b0316600160a01b90920465ffffffffffff16600160d01b0291909117905561369d565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec590600090a15b50600380546001600160a01b0316600160a01b65ffffffffffff948516026001600160d01b031617600160d01b9290931691909102919091179055565b6000806000806137097f0000000000000000000000000000000000000000000000000000000000000000613070565b905060006137367f0000000000000000000000000000000000000000000000000000000000000000613070565b9050600080613746600b54612828565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152919350915061378e9085846140ef565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b90910416918101919091526137d19085846140ef565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b90910416918101919091526138149086856140ef565b96509650965050505050909192565b8061383683670de0b6b3a76400006149a0565b613840919061497a565b600b555050565b6000806138628486604001516001600160701b0316856143c3565b95945050505050565b6000806138628486606001516001600160701b0316856143c3565b8161392657600061389f6003546001600160a01b031690565b6001600160a01b03161461390a5760405162461bcd60e51b815260206004820152602c60248201527f416363657373436f6e74726f6c3a2064656661756c742061646d696e20616c7260448201526b1958591e4819dc985b9d195960a21b6064820152608401610936565b600380546001600160a01b0319166001600160a01b0383161790555b61094982826143d9565b8115801561394b57506003546001600160a01b038281169116145b1561396157600380546001600160a01b03191690555b6109498282614444565b6040805160608101825282546001600160501b038082168352600160501b820481166020840152600160a01b909104169181019190915260009081906139b0906124ff565b90506139bb426133b8565b83546001600160501b0391909116600160501b02600160501b600160a01b0319909116179092555090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a689190614a34565b6008600001819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613acf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af39190614a34565b60095543600a55565b613b356040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031603613b77576000613bf9565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630fbc8f5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bf99190614a34565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031603613c3d576000613cbf565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630fbc8f5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cbf9190614a34565b90506000613ccd828461497a565b90506000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168c6001600160a01b031603613d19578a9350899150613db9565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168c6001600160a01b031603613d5c575089915088613db9565b613d678b87876144ab565b613d728a87876144ea565b84613d7d878d6149a0565b613d8791906149cd565b935084613d94878c6149a0565b613d9e91906149cd565b9150613daa848c61498d565b9250613db6828b61498d565b90505b6000808a8015613dc857508815155b15613e855760075461271090613de49063ffffffff16876149a0565b613dee91906149cd565b9150600085118015613dfe575081155b15613e1c57604051633853986560e01b815260040160405180910390fd5b613e26828661498d565b60075490955061271090613e409063ffffffff16856149a0565b613e4a91906149cd565b9050600083118015613e5a575080155b15613e785760405163041c928b60e11b815260040160405180910390fd5b613e82818461498d565b92505b6040805160c0810182529586526020860196909652948401526060830152608082015260a081019190915298975050505050505050565b6040805160608101825284546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090613eff906124ff565b905081613f0c828561497a565b1015613f2a5760405162da056d60e81b815260040160405180910390fd5b613f3e84613f38838661497a565b8461350d565b613f47826133b8565b845469ffffffffffffffffffff19166001600160501b039190911617909355505050565b60005460ff166121265760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610936565b600065ffffffffffff8211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203460448201526538206269747360d01b6064820152608401610936565b6000614025611bd1565b6002805465ffffffffffff8616600160a01b026001600160d01b03199091166001600160a01b03881617179055915061406790508165ffffffffffff16151590565b15612480576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a960510990600090a1505050565b6000806140a5611753565b90508065ffffffffffff168365ffffffffffff16116140c857612a0a8382614ac2565b61130765ffffffffffff8416620697805b60008183106140e85781611307565b5090919050565b60008260000361410d575060408301516001600160501b0316611307565b600061412685602001516001600160501b0316426140d9565b905082811161414457505060408301516001600160501b0316611307565b6000614150848361498d565b90506141758587600001516001600160501b03168361416f91906149a0565b906133a3565b86604001516001600160501b031661418d919061497a565b9695505050505050565b60006113078383670de0b6b3a76400006141f7565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316146141ef5760095461068d565b505060085490565b600082600019048411830215820261420e57600080fd5b5091020490565b606061068d6001600160a01b03831660145b606060006142368360026149a0565b61424190600261497a565b67ffffffffffffffff811115614259576142596148db565b6040519080825280601f01601f191660200182016040528015614283576020820181803683370190505b509050600360fc1b8160008151811061429e5761429e6148f1565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106142cd576142cd6148f1565b60200101906001600160f81b031916908160001a90535060006142f18460026149a0565b6142fc90600161497a565b90505b6001811115614374576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614330576143306148f1565b1a60f81b828281518110614346576143466148f1565b60200101906001600160f81b031916908160001a90535060049490941c9361436d81614ae1565b90506142ff565b5083156113075760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610936565b60006124526143d2848461498d565b8590614197565b6143e38282611220565b6109495760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b61444e8282611220565b156109495760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b82158015906144cc575081158015906144cc5750806144ca83856149a0565b105b1561248057604051633853986560e01b815260040160405180910390fd5b81158015906145015750806144ff83856149a0565b105b156124805760405163041c928b60e11b815260040160405180910390fd5b6040805160e08101825260008082526020820181905291810182905260608101829052906080820190815260006020820181905260409091015290565b60006020828403121561456e57600080fd5b81356001600160e01b03198116811461130757600080fd5b60005b838110156145a1578181015183820152602001614589565b50506000910152565b60208152600082518060208401526145c9816040850160208701614586565b601f01601f19169190910160400192915050565b80356001600160a01b03811681146145f457600080fd5b919050565b6000806040838503121561460c57600080fd5b614615836145dd565b946020939093013593505050565b60006020828403121561463557600080fd5b5035919050565b6000806040838503121561464f57600080fd5b8235915061465f602084016145dd565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156146a95783516001600160a01b031683529284019291840191600101614684565b50909695505050505050565b6000806000606084860312156146ca57600080fd5b6146d3846145dd565b95602085013595506040909401359392505050565b6000602082840312156146fa57600080fd5b611307826145dd565b634e487b7160e01b600052602160045260246000fd5b6001600160701b038082511683528060208301511660208401528060408301511660408401528060608301511660608401525060808101516003811061476f57634e487b7160e01b600052602160045260246000fd5b8060808401525060a081015161479060a08401826001600160701b03169052565b5060c081015161248060c08401826001600160701b03169052565b60e0810161068d8284614719565b6000602082840312156147cb57600080fd5b813565ffffffffffff8116811461130757600080fd5b61010081016147f08285614719565b8260e08301529392505050565b80151581146106a657600080fd5b600080600080600060a0868803121561482357600080fd5b61482c866145dd565b94506020860135935060408601359250606086013591506080860135614851816147fd565b809150509295509295909350565b815180516001600160501b039081168352602080830151821681850152604092830151821683850152808501518051831660608601528082015183166080860152830151821660a0850152828501518051831660c086015290810151821660e085015291820151166101008301526101208201905b5092915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561491957600080fd5b8151611307816147fd565b634e487b7160e01b600052601160045260246000fd5b6001600160701b038281168282160390808211156148d4576148d4614924565b6001600160701b038181168382160190808211156148d4576148d4614924565b8082018082111561068d5761068d614924565b8181038181111561068d5761068d614924565b808202811582820484141761068d5761068d614924565b634e487b7160e01b600052601260045260246000fd5b6000826149dc576149dc6149b7565b500490565b6001600160501b038181168382160190808211156148d4576148d4614924565b65ffffffffffff8181168382160190808211156148d4576148d4614924565b600082614a2f57614a2f6149b7565b500690565b600060208284031215614a4657600080fd5b5051919050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351614a85816017850160208801614586565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351614ab6816028840160208801614586565b01602801949350505050565b65ffffffffffff8281168282160390808211156148d4576148d4614924565b600081614af057614af0614924565b50600019019056fea2646970667358221220759dc3b88a3e08ba309d211f431f2485ef8307884fb25ec991b4b67724d1cfe864736f6c63430008130033000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc50000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061027f5760003560e01c806370a205b81161015c578063c00007b0116100ce578063d602b9fd11610087578063d602b9fd146105df578063de0b985a146105e7578063e19e3d7f146105fc578063e63ab1e91461060f578063fcb6813e14610636578063ffafc0b11461063e57600080fd5b8063c00007b014610568578063cc8463c81461057b578063cefc142914610583578063cf3bf2eb1461058b578063cf6eefb71461059e578063d547741f146105cc57600080fd5b806391d148541161012057806391d148541461050b578063a1eda53c1461051e578063a217fddf14610545578063a9d637e11461054d578063b187bd2614610402578063b88a802f1461056057600080fd5b806370a205b81461047057806384ef8ffc146104965780638580cf76146104bb57806387621cbb146104e25780638da5cb5b1461050357600080fd5b806343d726d6116101f55780635350e731116101b95780635350e731146103ef5780635c975abb146104025780635e2746541461040d578063634e93da1461042d578063649a5ec71461044057806365118b3f1461045357600080fd5b806343d726d6146103b257806344ea559a146103ba57806347535d7b146103cd5780634a4e3bd5146103df57806351858e27146103e757600080fd5b80631a399125116102475780631a3991251461031d578063248a9ca3146103305780632f2ff15d1461035457806336568abe146103675780633c70a5841461037a578063417243c91461039d57600080fd5b806301ffc9a714610284578063022d63fb146102ac5780630aa6220b146102c85780630c7e5c23146102d2578063181f5a77146102ea575b600080fd5b61029761029236600461455c565b610668565b60405190151581526020015b60405180910390f35b620697805b60405165ffffffffffff90911681526020016102a3565b6102d0610693565b005b60075463ffffffff165b6040519081526020016102a3565b604080518082018252601181527005265776172645661756c7420312e302e3607c1b602082015290516102a391906145aa565b6102d061032b3660046145f9565b6106a9565b6102dc61033e366004614623565b6000908152600160208190526040909120015490565b6102d061036236600461463c565b6108ce565b6102d061037536600461463c565b61094d565b610382610a37565b604080519384526020840192909252908201526060016102a3565b6103a5610b1c565b6040516102a39190614668565b6102d0610be6565b6102d06103c83660046146b5565b610d04565b600754600160201b900460ff16610297565b6102d0610f2f565b6102d0610f61565b6102976103fd3660046146e8565b610f93565b60005460ff16610297565b61042061041b3660046146e8565b611076565b6040516102a391906147ab565b6102d061043b3660046146e8565b61113d565b6102d061044e3660046147b9565b611151565b61045b611165565b604080519283526020830191909152016102a3565b7f000000000000000000000000000000000000000000000000000000000076a7006102dc565b6003546001600160a01b03165b6040516001600160a01b0390911681526020016102a3565b6102dc7fbeec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f681565b6104f56104f03660046146e8565b61117b565b6040516102a39291906147e1565b6104a3611207565b61029761051936600461463c565b611220565b61052661124b565b6040805165ffffffffffff9384168152929091166020830152016102a3565b6102dc600081565b6102dc61055b3660046146e8565b61129b565b6102dc61130e565b6102dc6105763660046146e8565b61167d565b6102b1611753565b6102d06117b2565b6102d061059936600461480b565b611830565b6105a6611bd1565b604080516001600160a01b03909316835265ffffffffffff9091166020830152016102a3565b6102d06105da36600461463c565b611bf2565b6102d0611c6f565b6105ef611c82565b6040516102a3919061485f565b6102d061060a366004614623565b611d88565b6102dc7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610297612055565b610646612093565b60408051825181526020808401519082015291810151908201526060016102a3565b60006001600160e01b031982166318a4c3c360e11b148061068d575061068d826120dc565b92915050565b600061069e81612111565b6106a661211b565b50565b336001600160a01b037f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5161480159061070b5750336001600160a01b037f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e1614155b1561072957604051631decfebb60e31b815260040160405180910390fd5b610731612128565b600061076983336001600160a01b037f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc516148461227d565b6001600160a01b0384166000908152600c602090815260409182902083518154928501516001600160701b039182166001600160e01b031994851617600160701b918316820217835593850151600183018054606088015192841695169490941791169093029290921780825560808401519394508493919060ff60e01b1916600160e01b83600281111561080057610800614703565b021790555060a0828101516002909201805460c0909401516001600160701b03908116600160701b026001600160e01b03199095169316929092179290921790558151602083015160408085015160608601519486015191516001600160a01b038916957f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a956108c19590949093926001600160701b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b60405180910390a2505050565b8161093f5760405162461bcd60e51b815260206004820152603660248201527f416363657373436f6e74726f6c3a2063616e2774206469726563746c79206772604482015275616e742064656661756c742061646d696e20726f6c6560501b60648201526084015b60405180910390fd5b610949828261245a565b5050565b8115801561096857506003546001600160a01b038281169116145b15610a2d57600080610978611bd1565b90925090506001600160a01b03821615801561099b575065ffffffffffff811615155b80156109ae57504265ffffffffffff8216105b610a185760405162461bcd60e51b815260206004820152603560248201527f416363657373436f6e74726f6c3a206f6e6c792063616e2072656e6f756e636560448201527420696e2074776f2064656c6179656420737465707360581b6064820152608401610936565b50506002805465ffffffffffff60a01b191690555b6109498282612485565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090819081908190610a81906124ff565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150600090610ac8906124ff565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150600090610b0f906124ff565b9296919550919350915050565b604080516002808252606080830184529260009291906020830190803683370190505090507f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc581600081518110610b7557610b756148f1565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e81600181518110610bc957610bc96148f1565b6001600160a01b0390921660209283029190910190910152919050565b6000610bf181612111565b600754600160201b900460ff16610c1b5760405163fc5c1c4560e01b815260040160405180910390fd5b6000610c25612551565b50506007805464ff00000000191690555060405163a9059cbb60e01b8152336004820152602481018290529092507f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca6001600160a01b0316915063a9059cbb906044016020604051808303816000875af1158015610ca7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccb9190614907565b506040518181527f4ef41257ba66fe15e89bff4221410f2c2a555d82f884ca0b3b29898d28635a8f906020015b60405180910390a15050565b610d2e7fbeec13769b5f410b0584f69811bfd923818456d5edcf426b0e31cf90eed7a3f633611220565b610d4b57604051631decfebb60e31b815260040160405180910390fd5b600754600160201b900460ff16610d755760405163fc5c1c4560e01b815260040160405180910390fd5b610d7d6125f1565b6001600160a01b03831615801590610dc757507f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b0316836001600160a01b031614155b8015610e0557507f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316836001600160a01b031614155b15610e225760405162820f3560e61b815260040160405180910390fd5b80600003610e435760405163041c928b60e11b815260040160405180910390fd5b610e4b612128565b610e56838383612637565b6040516323b872dd60e01b8152336004820152306024820152604481018390527f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca6001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610ec9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eed9190614907565b5060408051838152602081018390526001600160a01b038516917f6a6f77044107a33658235d41bedbbaf2fe9ccdceb313143c947a5e76e1ec847491016108c1565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f5981612111565b6106a66126bd565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610f8b81612111565b6106a661270f565b60007f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316826001600160a01b0316036110095760045442600160501b9091046001600160501b03161180159061068d57505060065442600160501b9091046001600160501b03161115919050565b7f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b0316826001600160a01b03160361105e57505060055442600160501b9091046001600160501b0316111590565b60405162820f3560e61b815260040160405180910390fd5b61107e61451f565b6001600160a01b0382166000908152600c6020908152604091829020825160e08101845281546001600160701b038082168352600160701b918290048116948301949094526001830154808516958301959095528404909216606083015290916080830190600160e01b900460ff1660028111156110fe576110fe614703565b600281111561110f5761110f614703565b8152600291909101546001600160701b038082166020840152600160701b9091041660409091015292915050565b600061114881612111565b6109498261274c565b600061115c81612111565b610949826127bf565b600080611173600b54612828565b915091509091565b61118361451f565b60008061118f84612860565b90506000816111be577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6111e0565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b905060006111ee868361297f565b90506111fb868285612a7a565b94509450505050915091565b600061121b6003546001600160a01b031690565b905090565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600354600090600160d01b900465ffffffffffff16801515801561127757504265ffffffffffff821610155b61128357600080611173565b600354600160a01b900465ffffffffffff1692909150565b6000806112a783612860565b6112d1577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6112f3565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b90506113076113028483612c6b565b612ce7565b9392505050565b60006113186125f1565b600061132333612860565b905061133b81611334576001612d77565b6002612d77565b600081611368577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e61138a565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b90506000611398338361297f565b905060006113a733858461227d565b905060006113c1826113bc6113023388612c6b565b613035565b9050808260c0018181516113d5919061493a565b6001600160701b031690525060a0820180518291906113f590839061495a565b6001600160701b0316905250815160009061141190839061495a565b600084526001600160701b03169050851561144857602083015161143e906001600160701b03168261497a565b6000602085015290505b8060000361146957604051635aa9184d60e01b815260040160405180910390fd5b336000908152600c602090815260409182902085518154928701516001600160701b039182166001600160e01b031994851617600160701b91831682021783559387015160018301805460608a01519284169516949094179116909302929092178082556080860151869392909160ff60e01b1916600160e01b8360028111156114f5576114f5614703565b021790555060a0820151600291909101805460c0909301516001600160701b03908116600160701b026001600160e01b031990941692169190911791909117905560405163a9059cbb60e01b8152336004820152602481018290526001600160a01b037f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca169063a9059cbb906044016020604051808303816000875af11580156115a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115c79190614907565b5060405181815233907f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f72419060200160405180910390a2604080840151606085015160a0860151925133937f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a9361166b9360009384939094855260208501939093526001600160701b0391821660408501528116606084015216608082015260a00190565b60405180910390a29550505050505090565b60008061168983612860565b90506000816116b8577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6116da565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b905060006116e8858361297f565b90506000806116f8878487612a7a565b91509150600061171b828561170c88613070565b611716919061498d565b6130ef565b925050508083602001518460000151611734919061495a565b6001600160701b0316611747919061497a565b98975050505050505050565b600354600090600160d01b900465ffffffffffff16801515801561177e57504265ffffffffffff8216105b61179957600254600160d01b900465ffffffffffff166117ac565b600354600160a01b900465ffffffffffff165b91505090565b60006117bc611bd1565b509050336001600160a01b038216146118285760405162461bcd60e51b815260206004820152602860248201527f416363657373436f6e74726f6c3a2070656e64696e672061646d696e206d75736044820152671d081858d8d95c1d60c21b6064820152608401610936565b6106a661313a565b336001600160a01b037f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc516148015906118925750336001600160a01b037f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e1614155b156118b057604051631decfebb60e31b815260040160405180910390fd5b336001600160a01b037f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc516146118eb81611334576001612d77565b60006118f887838861227d565b90506000611909826113bc88612ce7565b9050808260c00181815161191d919061493a565b6001600160701b031690525081518190839061193a90839061495a565b6001600160701b0316905250600060a0830152836119655761195d888386613205565b505050611bca565b60c08201516001600160701b03811660000361198f57611986898487613205565b50505050611bca565b6000846119bc577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6119de565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc55b90506000896119ec83613070565b6119f6919061498d565b905080600003611a8757600060c0860152845183908690611a1890839061495a565b6001600160701b0390811690915260408051600080825260208201529186169082015287151560608201527feb34a4cfa3869c57492c653f9c06f128abac61952688567d4396e9ffaf93a67a915060800160405180910390a1611a7c8b8689613205565b505050505050611bca565b60008a15611ab2578a611aa38a6001600160701b0387166149a0565b611aad91906149cd565b611abd565b836001600160701b03165b9050600087611acd576005611ad0565b60045b90506000611ade83856133a3565b9050611ae9816133b8565b82548390601490611b0b908490600160a01b90046001600160501b03166149e1565b92506101000a8154816001600160501b0302191690836001600160501b031602179055507feb34a4cfa3869c57492c653f9c06f128abac61952688567d4396e9ffaf93a67a838260008c604051611b7d9493929190938452602084019290925260408301521515606082015260800190565b60405180910390a1611b8f888a613424565b611b9883613473565b8860c001818151611ba9919061493a565b6001600160701b0316905250611bc08e898c613205565b5050505050505050505b5050505050565b6002546001600160a01b03811691600160a01b90910465ffffffffffff1690565b81611c655760405162461bcd60e51b815260206004820152603760248201527f416363657373436f6e74726f6c3a2063616e2774206469726563746c7920726560448201527f766f6b652064656661756c742061646d696e20726f6c650000000000000000006064820152608401610936565b61094982826134dc565b6000611c7a81612111565b6106a6613502565b611ced6040805160c081019091526000606082018181526080830182905260a083019190915281908152604080516060810182526000808252602082810182905292820152910190815260408051606081018252600080825260208281018290529282015291015290565b506040805160c0810182526004546001600160501b038082166060808501918252600160501b80850484166080870152600160a01b94859004841660a08701529185528551808201875260055480851682528381048516602083810191909152908690048516828901528087019190915286519182018752600654808516835292830484169082015292900416818401529181019190915290565b6000611d9381612111565b60075463ffffffff1682811480611dab575061271083115b15611dc95760405163027953ef60e61b815260040160405180910390fd5b600654600554600091611de8916001600160501b0391821691166149e1565b6001600160501b0316905060008415611e1757612710611e0886846149a0565b611e1291906149cd565b611e1a565b60005b905080158015611e2957508415155b8015611e3457508115155b15611e525760405163ba00abbd60e01b815260040160405180910390fd5b611e5a612128565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090611e9e906124ff565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152611edf906124ff565b611ee9919061497a565b90506000611ef7838561498d565b9050611f02816133b8565b6005805469ffffffffffffffffffff19166001600160501b0392909216919091179055611f2e836133b8565b6006805469ffffffffffffffffffff19166001600160501b03929092169190911790556000879003611f7e5760068054600160501b600160a01b0319169055611f796005838361350d565b611ff0565b6127108703611fa65760058054600160501b600160a01b0319169055611f796006838561350d565b8115611ff0576000612710611fbb89856149a0565b611fc591906149cd565b90506000611fd3828561498d565b9050611fe16005828561350d565b611fed6006838761350d565b50505b611ff98761355c565b6007805463ffffffff191663ffffffff9290921691909117905560408051868152602081018990527f03402a75be6462f03cfaa1a1d189e17ecf71c8b57bf127e93f5f5aeae974b43c910160405180910390a150505050505050565b6004546000906001600160501b031615158061207b57506005546001600160501b031615155b8061121b5750506006546001600160501b0316151590565b6120b760405180606001604052806000815260200160008152602001600081525090565b506040805160608101825260085481526009546020820152600a549181019190915290565b60006001600160e01b03198216637965db0b60e01b148061068d57506301ffc9a760e01b6001600160e01b031983161461068d565b6106a681336135c1565b61212660008061361a565b565b600080612136600b54612828565b91509150428214801561214857504281145b15612151575050565b600080600061215e6136da565b92509250925061216d836133b8565b600580546001600160501b0392909216600160a01b02600160a01b600160f01b03199092169190911790556121a1826133b8565b600480546001600160501b0392909216600160a01b02600160a01b600160f01b03199092169190911790556121d5816133b8565b600680546001600160501b0392909216600160a01b02600160a01b600160f01b031990921691909117905561220a4280613823565b6040518381527f1236d5f7290187e9ac80880b3e28a193a9f38bb8bdc8a8a197744d80c50ca3269060200160405180910390a160408051838152602081018390527f9eecdebdae54d18e0c665dae22811251367c65647ec09aa5e33f001a5c147d22910160405180910390a15050505050565b61228561451f565b6001600160a01b0384166000908152600c60209081526040808320815160e08101835281546001600160701b038082168352600160701b9182900481169583019590955260018301548086169483019490945283049093166060840152906080830190600160e01b900460ff16600281111561230357612303614703565b600281111561231457612314614703565b8152600291909101546001600160701b038082166020840152600160701b90910416604090910152905060008160800151600281111561235657612356614703565b03612397578361236757600161236a565b60025b8160800190600281111561238057612380614703565b9081600281111561239357612393614703565b9052505b6123e36123de8285876123bc57600554600160a01b90046001600160501b03166123d0565b600454600160a01b90046001600160501b03165b6001600160501b0316613847565b613473565b8160c0018181516123f4919061495a565b6001600160701b031690525083156124485760065461242a906123de9083908690600160a01b90046001600160501b031661386b565b8160200181815161243b919061495a565b6001600160701b03169052505b6124528185613424565b949350505050565b6000828152600160208190526040909120015461247681612111565b6124808383613886565b505050565b6001600160a01b03811633146124f55760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610936565b6109498282613930565b60004282602001516001600160501b03161115612549574282602001516001600160501b031661252f919061498d565b825161254491906001600160501b03166149a0565b61068d565b600092915050565b6000806000806000612561612128565b600061256d600461396b565b9050600061257b600561396b565b90506000612589600661396b565b9050600081612598848661497a565b6125a2919061497a565b90506125ac6139e6565b6006546005546004546001600160501b03928316926125ce92811691166149e1565b6125d891906149e1565b6001600160501b03169990985092965090945092509050565b60005460ff16156121265760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610936565b6007546000906126549085908590859063ffffffff161515613afc565b90508060600151600014612675578051606082015161267591600591613ebc565b60808101511561269657612696600460000182602001518360800151613ebc565b60a0810151156126b7576126b7600460020182604001518360a00151613ebc565b50505050565b6126c5613f6b565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6127176125f1565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586126f23390565b6000612756611753565b61275f42613fb4565b6127699190614a01565b9050612775828261401b565b60405165ffffffffffff821681526001600160a01b038316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b60006127ca8261409a565b6127d342613fb4565b6127dd9190614a01565b90506127e9828261361a565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9101610cf8565b6000808061283e670de0b6b3a7640000856149cd565b90506000612854670de0b6b3a764000086614a20565b91959194509092505050565b6040516336b87bd760e11b81526001600160a01b0382811660048301526000917f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc590911690636d70f7ae90602401602060405180830381865afa1580156128cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ef9190614907565b8061068d5750604051637d3374e560e01b81526001600160a01b0383811660048301527f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc51690637d3374e590602401602060405180830381865afa15801561295b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068d9190614907565b600754600090600160201b900460ff16612a0f57600a54604051634c5b725f60e01b81526001600160a01b038581166004830152602482019290925290831690634c5b725f906044015b602060405180830381865afa1580156129e6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a0a9190614a34565b611307565b604051630706983f60e51b81526001600160a01b03848116600483015283169063e0d307e0906024015b602060405180830381865afa158015612a56573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113079190614a34565b612a8261451f565b6001600160a01b0384166000908152600c60209081526040808320815160e08101835281546001600160701b038082168352600160701b91829004811695830195909552600183015480861694830194909452830490931660608401528392916080830190600160e01b900460ff166002811115612b0257612b02614703565b6002811115612b1357612b13614703565b8152600291909101546001600160701b038082166020840152600160701b90910416604090910152905060008080612b496136da565b925092509250612b686123de858a8a612b625786613847565b85613847565b8460c001818151612b79919061495a565b6001600160701b03169052508615612bb757612b996123de858a8461386b565b84602001818151612baa919061495a565b6001600160701b03169052505b6000612c15856113bc6113028d8c612bef577f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e612c6b565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5612c6b565b90508085600001818151612c29919061495a565b6001600160701b031690525060c0850151600090612c4890839061493a565b600060c0880152959b6001600160701b039096169a509498505050505050505050565b600754600090600160201b900460ff16612cb957600a54604051634b669bff60e11b81526001600160a01b0385811660048301526024820192909252908316906396cd37fe906044016129c9565b604051631624e46160e21b81526001600160a01b038481166004830152831690635893918490602401612a39565b600081600003612cf957506000919050565b600754600160201b900460ff16612d195750670de0b6b3a7640000919050565b7f000000000000000000000000000000000000000000000000000000000076a7006000819003612d535750670de0b6b3a764000092915050565b611307612d69612d63854261498d565b836133a3565b670de0b6b3a76400006140d9565b600080612d85600b54612828565b90925090506001836002811115612d9e57612d9e614703565b03612ea357428203612daf57505050565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152612e2390612e1e90612e187f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e613070565b856140ef565b6133b8565b600580546001600160501b0392909216600160a01b02600160a01b600160f01b0319909216919091179055612e584282613823565b600554604051600160a01b9091046001600160501b031681527f1236d5f7290187e9ac80880b3e28a193a9f38bb8bdc8a8a197744d80c50ca3269060200160405180910390a1505050565b6002836002811115612eb757612eb7614703565b0361248057428103612ec857505050565b6000612ef37f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5613070565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152909150612f3d90612e1e9083856140ef565b60048054600160a01b600160f01b031916600160a01b6001600160501b03938416810291909117909155604080516060810182526006548085168252600160501b8104851660208301529290920490921691810191909152612fa490612e1e9083856140ef565b600680546001600160501b0392909216600160a01b02600160a01b600160f01b0319909216919091179055612fd98342613823565b600454600654604080516001600160501b03600160a01b94859004811682529390920490921660208201527f9eecdebdae54d18e0c665dae22811251367c65647ec09aa5e33f001a5c147d22910160405180910390a150505050565b60008260a001516130666123de848660a001518760c00151613057919061495a565b6001600160701b031690614197565b611307919061493a565b600754600090600160201b900460ff1661308d57612544826141ac565b816001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130cb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068d9190614a34565b60008060008460000361310a57506000915081905080613133565b6000808086156131285787925061312183886133a3565b915061312b565b50865b919450925090505b9250925092565b600080613145611bd1565b9150915061315a8165ffffffffffff16151590565b801561316d57504265ffffffffffff8216105b6131ca5760405162461bcd60e51b815260206004820152602860248201527f416363657373436f6e74726f6c3a207472616e736665722064656c6179206e6f6044820152671d081c185cdcd95960c21b6064820152608401610936565b6131e660006131e16003546001600160a01b031690565b613930565b6131f1600083613886565b5050600280546001600160d01b0319169055565b6001600160a01b0383166000908152600c602090815260409182902084518154928601516001600160701b039182166001600160e01b031994851617600160701b91831682021783559386015160018301805460608901519284169516949094179116909302929092178082556080850151859392909160ff60e01b1916600160e01b83600281111561329a5761329a614703565b021790555060a08201516002909101805460c0909301516001600160701b03908116600160701b026001600160e01b03199094169216919091179190911790556040516001600160a01b038416907fa6b1f21803a1471cf6981bffc4c07ff6610f2b88efc44f62b96dca65a63d3d279061331990841515815260200190565b60405180910390a2826001600160a01b03167f16aadfd997cc8ab0f2890a6c7fe1ea76bdb61f8c74cc386c92818a2b1767006a83600001518460200151856040015186606001518760a001516040516108c19594939291906001600160701b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b600061130783670de0b6b3a7640000846141f7565b60006001600160501b038211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201526530206269747360d01b6064820152608401610936565b5090565b8015613456576004546001600160501b03600160a01b9182900481166040850152600654919091041660608301525050565b6005546001600160501b03600160a01b9091041660408301525050565b60006001600160701b038211156134205760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663132206269747360c81b6064820152608401610936565b600082815260016020819052604090912001546134f881612111565b6124808383613930565b61212660008061401b565b8060000361351a57505050565b61353161352782846149cd565b612e1e904261497a565b83546001600160501b0391909116600160501b02600160501b600160a01b0319909116179092555050565b600063ffffffff8211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610936565b6135cb8282611220565b610949576135d881614215565b6135e3836020614227565b6040516020016135f4929190614a4d565b60408051601f198184030181529082905262461bcd60e51b8252610936916004016145aa565b600354600160d01b900465ffffffffffff16801561369d574265ffffffffffff8216101561367357600354600280546001600160d01b0316600160a01b90920465ffffffffffff16600160d01b0291909117905561369d565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec590600090a15b50600380546001600160a01b0316600160a01b65ffffffffffff948516026001600160d01b031617600160d01b9290931691909102919091179055565b6000806000806137097f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e613070565b905060006137367f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5613070565b9050600080613746600b54612828565b604080516060810182526005546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152919350915061378e9085846140ef565b604080516060810182526004546001600160501b038082168352600160501b820481166020840152600160a01b90910416918101919091526137d19085846140ef565b604080516060810182526006546001600160501b038082168352600160501b820481166020840152600160a01b90910416918101919091526138149086856140ef565b96509650965050505050909192565b8061383683670de0b6b3a76400006149a0565b613840919061497a565b600b555050565b6000806138628486604001516001600160701b0316856143c3565b95945050505050565b6000806138628486606001516001600160701b0316856143c3565b8161392657600061389f6003546001600160a01b031690565b6001600160a01b03161461390a5760405162461bcd60e51b815260206004820152602c60248201527f416363657373436f6e74726f6c3a2064656661756c742061646d696e20616c7260448201526b1958591e4819dc985b9d195960a21b6064820152608401610936565b600380546001600160a01b0319166001600160a01b0383161790555b61094982826143d9565b8115801561394b57506003546001600160a01b038281169116145b1561396157600380546001600160a01b03191690555b6109498282614444565b6040805160608101825282546001600160501b038082168352600160501b820481166020840152600160a01b909104169181019190915260009081906139b0906124ff565b90506139bb426133b8565b83546001600160501b0391909116600160501b02600160501b600160a01b0319909116179092555090565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a689190614a34565b6008600001819055507f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b031663885491af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613acf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af39190614a34565b60095543600a55565b613b356040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60007f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316866001600160a01b031603613b77576000613bf9565b7f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b0316630fbc8f5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bf99190614a34565b905060007f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b0316876001600160a01b031603613c3d576000613cbf565b7f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316630fbc8f5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cbf9190614a34565b90506000613ccd828461497a565b90506000806000807f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b03168c6001600160a01b031603613d19578a9350899150613db9565b7f000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e6001600160a01b03168c6001600160a01b031603613d5c575089915088613db9565b613d678b87876144ab565b613d728a87876144ea565b84613d7d878d6149a0565b613d8791906149cd565b935084613d94878c6149a0565b613d9e91906149cd565b9150613daa848c61498d565b9250613db6828b61498d565b90505b6000808a8015613dc857508815155b15613e855760075461271090613de49063ffffffff16876149a0565b613dee91906149cd565b9150600085118015613dfe575081155b15613e1c57604051633853986560e01b815260040160405180910390fd5b613e26828661498d565b60075490955061271090613e409063ffffffff16856149a0565b613e4a91906149cd565b9050600083118015613e5a575080155b15613e785760405163041c928b60e11b815260040160405180910390fd5b613e82818461498d565b92505b6040805160c0810182529586526020860196909652948401526060830152608082015260a081019190915298975050505050505050565b6040805160608101825284546001600160501b038082168352600160501b820481166020840152600160a01b9091041691810191909152600090613eff906124ff565b905081613f0c828561497a565b1015613f2a5760405162da056d60e81b815260040160405180910390fd5b613f3e84613f38838661497a565b8461350d565b613f47826133b8565b845469ffffffffffffffffffff19166001600160501b039190911617909355505050565b60005460ff166121265760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610936565b600065ffffffffffff8211156134205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203460448201526538206269747360d01b6064820152608401610936565b6000614025611bd1565b6002805465ffffffffffff8616600160a01b026001600160d01b03199091166001600160a01b03881617179055915061406790508165ffffffffffff16151590565b15612480576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a960510990600090a1505050565b6000806140a5611753565b90508065ffffffffffff168365ffffffffffff16116140c857612a0a8382614ac2565b61130765ffffffffffff8416620697805b60008183106140e85781611307565b5090919050565b60008260000361410d575060408301516001600160501b0316611307565b600061412685602001516001600160501b0316426140d9565b905082811161414457505060408301516001600160501b0316611307565b6000614150848361498d565b90506141758587600001516001600160501b03168361416f91906149a0565b906133a3565b86604001516001600160501b031661418d919061497a565b9695505050505050565b60006113078383670de0b6b3a76400006141f7565b60007f000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc56001600160a01b0316826001600160a01b0316146141ef5760095461068d565b505060085490565b600082600019048411830215820261420e57600080fd5b5091020490565b606061068d6001600160a01b03831660145b606060006142368360026149a0565b61424190600261497a565b67ffffffffffffffff811115614259576142596148db565b6040519080825280601f01601f191660200182016040528015614283576020820181803683370190505b509050600360fc1b8160008151811061429e5761429e6148f1565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106142cd576142cd6148f1565b60200101906001600160f81b031916908160001a90535060006142f18460026149a0565b6142fc90600161497a565b90505b6001811115614374576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614330576143306148f1565b1a60f81b828281518110614346576143466148f1565b60200101906001600160f81b031916908160001a90535060049490941c9361436d81614ae1565b90506142ff565b5083156113075760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610936565b60006124526143d2848461498d565b8590614197565b6143e38282611220565b6109495760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b61444e8282611220565b156109495760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b82158015906144cc575081158015906144cc5750806144ca83856149a0565b105b1561248057604051633853986560e01b815260040160405180910390fd5b81158015906145015750806144ff83856149a0565b105b156124805760405163041c928b60e11b815260040160405180910390fd5b6040805160e08101825260008082526020820181905291810182905260608101829052906080820190815260006020820181905260409091015290565b60006020828403121561456e57600080fd5b81356001600160e01b03198116811461130757600080fd5b60005b838110156145a1578181015183820152602001614589565b50506000910152565b60208152600082518060208401526145c9816040850160208701614586565b601f01601f19169190910160400192915050565b80356001600160a01b03811681146145f457600080fd5b919050565b6000806040838503121561460c57600080fd5b614615836145dd565b946020939093013593505050565b60006020828403121561463557600080fd5b5035919050565b6000806040838503121561464f57600080fd5b8235915061465f602084016145dd565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156146a95783516001600160a01b031683529284019291840191600101614684565b50909695505050505050565b6000806000606084860312156146ca57600080fd5b6146d3846145dd565b95602085013595506040909401359392505050565b6000602082840312156146fa57600080fd5b611307826145dd565b634e487b7160e01b600052602160045260246000fd5b6001600160701b038082511683528060208301511660208401528060408301511660408401528060608301511660608401525060808101516003811061476f57634e487b7160e01b600052602160045260246000fd5b8060808401525060a081015161479060a08401826001600160701b03169052565b5060c081015161248060c08401826001600160701b03169052565b60e0810161068d8284614719565b6000602082840312156147cb57600080fd5b813565ffffffffffff8116811461130757600080fd5b61010081016147f08285614719565b8260e08301529392505050565b80151581146106a657600080fd5b600080600080600060a0868803121561482357600080fd5b61482c866145dd565b94506020860135935060408601359250606086013591506080860135614851816147fd565b809150509295509295909350565b815180516001600160501b039081168352602080830151821681850152604092830151821683850152808501518051831660608601528082015183166080860152830151821660a0850152828501518051831660c086015290810151821660e085015291820151166101008301526101208201905b5092915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561491957600080fd5b8151611307816147fd565b634e487b7160e01b600052601160045260246000fd5b6001600160701b038281168282160390808211156148d4576148d4614924565b6001600160701b038181168382160190808211156148d4576148d4614924565b8082018082111561068d5761068d614924565b8181038181111561068d5761068d614924565b808202811582820484141761068d5761068d614924565b634e487b7160e01b600052601260045260246000fd5b6000826149dc576149dc6149b7565b500490565b6001600160501b038181168382160190808211156148d4576148d4614924565b65ffffffffffff8181168382160190808211156148d4576148d4614924565b600082614a2f57614a2f6149b7565b500690565b600060208284031215614a4657600080fd5b5051919050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351614a85816017850160208801614586565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351614ab6816028840160208801614586565b01602801949350505050565b65ffffffffffff8281168282160390808211156148d4576148d4614924565b600081614af057614af0614924565b50600019019056fea2646970667358221220759dc3b88a3e08ba309d211f431f2485ef8307884fb25ec991b4b67724d1cfe864736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc50000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000076a7000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : params (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca
Arg [1] : 000000000000000000000000bc10f2e862ed4502144c7d632a3459f49dfcdb5e
Arg [2] : 000000000000000000000000a1d76a7ca72128541e9fcacafbda3a92ef94fdc5
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000190
Arg [4] : 000000000000000000000000000000000000000000000000000000000076a700
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 25 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $13.63 | 1,069,155.4197 | $14,572,588.37 |
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.