Contract Address Details

0x7430B6D098a8663faCca59BdD64fbB535865c2e1

Contract Name
StakingHbbft
Creator
0xbc1b9d–5a2620 at 0xbe8c5f–8ae7b2
Balance
0 tDMD
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
126768
Contract name:
StakingHbbft




Optimization enabled
true
Compiler version
v0.8.25+commit.b61c2a91




Optimization runs
800
Verified at
2025-02-04 01:31:33.698058Z

contracts/StakingHbbft.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { IBlockRewardHbbft } from "./interfaces/IBlockRewardHbbft.sol";
import { IStakingHbbft } from "./interfaces/IStakingHbbft.sol";
import { IValidatorSetHbbft } from "./interfaces/IValidatorSetHbbft.sol";
import { IBonusScoreSystem } from "./interfaces/IBonusScoreSystem.sol";
import { Unauthorized, ZeroAddress, ZeroGasPrice } from "./lib/Errors.sol";
import { TransferUtils } from "./utils/TransferUtils.sol";
import { ValueGuards } from "./lib/ValueGuards.sol";
/// @dev Implements staking and withdrawal logic.
// slither-disable-start unused-return
contract StakingHbbft is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable, IStakingHbbft, ValueGuards {
using EnumerableSet for EnumerableSet.AddressSet;
EnumerableSet.AddressSet private _pools;
EnumerableSet.AddressSet private _poolsInactive;
EnumerableSet.AddressSet private _poolsToBeRemoved;
address[] private _poolsToBeElected;
uint256[] private _poolsLikelihood;
uint256 private _poolsLikelihoodSum;
mapping(address => EnumerableSet.AddressSet) private _poolDelegators;
mapping(address => EnumerableSet.AddressSet) private _poolDelegatorsInactive;
mapping(address => mapping(address => mapping(uint256 => uint256))) private _stakeAmountByEpoch;
/// @dev The limit of the minimum candidate stake (CANDIDATE_MIN_STAKE).
uint256 public candidateMinStake;
/// @dev The limit of the minimum delegator stake (DELEGATOR_MIN_STAKE).
uint256 public delegatorMinStake;
/// @dev current limit of how many funds can
/// be staked on a single validator.
uint256 public maxStakeAmount;
/// @dev The current amount of staking coins ordered for withdrawal from the specified
/// pool by the specified staker. Used by the `orderWithdraw`, `claimOrderedWithdraw` and other functions.
/// The first parameter is the pool staking address, the second one is the staker address.
mapping(address => mapping(address => uint256)) public orderedWithdrawAmount;
/// @dev The current total amount of staking coins ordered for withdrawal from
/// the specified pool by all of its stakers. Pool staking address is accepted as a parameter.
mapping(address => uint256) public orderedWithdrawAmountTotal;
/// @dev The number of the staking epoch during which the specified staker ordered
/// the latest withdraw from the specified pool. Used by the `claimOrderedWithdraw` function
/// to allow the ordered amount to be claimed only in future staking epochs. The first parameter
/// is the pool staking address, the second one is the staker address.
mapping(address => mapping(address => uint256)) public orderWithdrawEpoch;
/// @dev The pool's index in the array returned by the `getPoolsToBeElected` getter.
/// Used by the `_deletePoolToBeElected` and `_isPoolToBeElected` internal functions.
/// The pool staking address is accepted as a parameter.
/// If the value is zero, it may mean the array doesn't contain the address.
/// Check the address is in the array using the `getPoolsToBeElected` getter.
mapping(address => uint256) public poolToBeElectedIndex;
/// @dev The amount of coins currently staked into the specified pool by the specified
/// staker. Doesn't include the amount ordered for withdrawal.
/// The first parameter is the pool staking address, the second one is the staker address.
mapping(address => mapping(address => uint256)) public stakeAmount;
/// @dev The duration period (in blocks) at the end of staking epoch during which
/// participants are not allowed to stake/withdraw/order/claim their staking coins.
uint256 public stakingWithdrawDisallowPeriod;
/// @dev The serial number of the current staking epoch.
uint256 public stakingEpoch;
/// @dev The fixed duration of each staking epoch before KeyGen starts i.e.
/// before the upcoming ("pending") validators are selected.
uint256 public stakingFixedEpochDuration;
/// @dev Length of the timeframe in seconds for the transition to the new validator set.
uint256 public stakingTransitionTimeframeLength;
/// @dev The timestamp of the last block of the the previous epoch.
/// The timestamp of the current epoch must be '>=' than this.
uint256 public stakingEpochStartTime;
/// @dev the blocknumber of the first block in this epoch.
/// this is mainly used for a historic lookup in the key gen history to read out the
/// ACKS and PARTS so a client is able to verify an epoch, even in the case that
/// the transition to the next epoch has already started,
/// and the information of the old keys is not available anymore.
uint256 public stakingEpochStartBlock;
/// @dev the extra time window pending validators have to write
/// to write their honey badger key shares.
/// this value is increased in response to a failed key generation event,
/// if one or more validators miss out writing their key shares.
uint256 public currentKeyGenExtraTimeWindow;
/// @dev Returns the total amount of staking coins currently staked into the specified pool.
/// Doesn't include the amount ordered for withdrawal.
/// The pool staking address is accepted as a parameter.
mapping(address => uint256) public stakeAmountTotal;
/// @dev Returns the total amount of staking coins currently staked on all pools.
/// Doesn't include the amount ordered for withdrawal.
uint256 public totalStakedAmount;
/// @dev The address of the `ValidatorSetHbbft` contract.
IValidatorSetHbbft public validatorSetContract;
struct PoolInfo {
bytes publicKey;
bytes16 internetAddress;
bytes2 port;
}
mapping(address => PoolInfo) public poolInfo;
mapping(address => bool) public abandonedAndRemoved;
/// @dev The total amount staked into the specified pool (staking address)
/// before the specified staking epoch. Filled by the `_snapshotPoolStakeAmounts` function.
mapping(uint256 => mapping(address => uint256)) public snapshotPoolTotalStakeAmount;
/// @dev The validator's amount staked into the specified pool (staking address)
/// before the specified staking epoch. Filled by the `_snapshotPoolStakeAmounts` function.
mapping(uint256 => mapping(address => uint256)) public snapshotPoolValidatorStakeAmount;
/// @dev The delegator's staked amount snapshot for specified epoch
/// pool => delegator => epoch => stake amount
mapping(address => mapping(address => mapping(uint256 => uint256))) internal _delegatorStakeSnapshot;
/// @dev Number of last epoch when stake snapshot was taken. pool => delegator => epoch
mapping(address => mapping(address => uint256)) internal _stakeSnapshotLastEpoch;
IBonusScoreSystem public bonusScoreContract;
/// @dev Address of node operator for specified pool.
mapping(address => address) public poolNodeOperator;
/// @dev Node operator share percent of total pool rewards.
mapping(address => uint256) public poolNodeOperatorShare;
/// @dev The epoch number in which the operator's address can be changed.
mapping(address => uint256) public poolNodeOperatorLastChangeEpoch;
// ============================================== Constants =======================================================
/// @dev The max number of candidates (including validators). This limit was determined through stress testing.
uint256 public constant MAX_CANDIDATES = 3000;
uint256 public constant MAX_NODE_OPERATOR_SHARE_PERCENT = 2000;
uint256 public constant PERCENT_DENOMINATOR = 10000;
// ================================================ Events ========================================================
/// @dev Emitted by the `claimOrderedWithdraw` function to signal the staker withdrew the specified
/// amount of requested coins from the specified pool during the specified staking epoch.
/// @param fromPoolStakingAddress The pool from which the `staker` withdrew the `amount`.
/// @param staker The address of the staker that withdrew the `amount`.
/// @param stakingEpoch The serial number of the staking epoch during which the claim was made.
/// @param amount The withdrawal amount.
event ClaimedOrderedWithdrawal(
address indexed fromPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
uint256 amount
);
/// @dev Emitted by the `moveStake` function to signal the staker moved the specified
/// amount of stake from one pool to another during the specified staking epoch.
/// @param fromPoolStakingAddress The pool from which the `staker` moved the stake.
/// @param toPoolStakingAddress The destination pool where the `staker` moved the stake.
/// @param staker The address of the staker who moved the `amount`.
/// @param stakingEpoch The serial number of the staking epoch during which the `amount` was moved.
/// @param amount The stake amount which was moved.
event MovedStake(
address fromPoolStakingAddress,
address indexed toPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
uint256 amount
);
/// @dev Emitted by the `orderWithdraw` function to signal the staker ordered the withdrawal of the
/// specified amount of their stake from the specified pool during the specified staking epoch.
/// @param fromPoolStakingAddress The pool from which the `staker` ordered a withdrawal of the `amount`.
/// @param staker The address of the staker that ordered the withdrawal of the `amount`.
/// @param stakingEpoch The serial number of the staking epoch during which the order was made.
/// @param amount The ordered withdrawal amount. Can be either positive or negative.
/// See the `orderWithdraw` function.
event OrderedWithdrawal(
address indexed fromPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
int256 amount
);
/// @dev Emitted by the `stake` function to signal the staker placed a stake of the specified
/// amount for the specified pool during the specified staking epoch.
/// @param toPoolStakingAddress The pool in which the `staker` placed the stake.
/// @param staker The address of the staker that placed the stake.
/// @param stakingEpoch The serial number of the staking epoch during which the stake was made.
/// @param amount The stake amount.
event PlacedStake(
address indexed toPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
uint256 amount
);
/// @dev Emitted by the `withdraw` function to signal the staker withdrew the specified
/// amount of a stake from the specified pool during the specified staking epoch.
/// @param fromPoolStakingAddress The pool from which the `staker` withdrew the `amount`.
/// @param staker The address of staker that withdrew the `amount`.
/// @param stakingEpoch The serial number of the staking epoch during which the withdrawal was made.
/// @param amount The withdrawal amount.
event WithdrewStake(
address indexed fromPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
uint256 amount
);
event GatherAbandonedStakes(address indexed caller, address indexed stakingAddress, uint256 gatheredFunds);
event RecoverAbandonedStakes(address indexed caller, uint256 reinsertShare, uint256 governanceShare);
/// @dev Emitted by the `restake` function to signal the epoch reward was restaked to the pool.
/// @param poolStakingAddress The pool for which the restake will be performed.
/// @param stakingEpoch The serial number of the staking epoch during which the restake was made.
/// @param validatorReward The amount of tokens restaked for the validator.
/// @param delegatorsReward The total amount of tokens restaked for the `poolStakingAddress` delegators.
event RestakeReward(
address indexed poolStakingAddress,
uint256 indexed stakingEpoch,
uint256 validatorReward,
uint256 delegatorsReward
);
/// @dev Emitted by the `_setNodeOperator` function.
/// @param poolStakingAddress The pool for which node operator was configured.
/// @param nodeOperatorAddress Address of node operator address related to `poolStakingAddress`.
/// @param operatorShare Node operator share percent.
event SetNodeOperator(
address indexed poolStakingAddress,
address indexed nodeOperatorAddress,
uint256 operatorShare
);
/**
* @dev Emitted when the minimum stake for a delegator is updated.
* @param minStake The new minimum stake value.
*/
event SetDelegatorMinStake(uint256 minStake);
// ============================================== Errors =======================================================
error CannotClaimWithdrawOrderYet(address pool, address staker);
error OnlyOncePerEpoch(uint256 _epoch);
error MaxPoolsCountExceeded();
error MaxAllowedWithdrawExceeded(uint256 allowed, uint256 desired);
error NoStakesToRecover();
error NotPayable();
error PoolAbandoned(address pool);
error PoolCannotBeRemoved(address pool);
error PoolEmpty(address pool);
error PoolNotExist(address pool);
error PoolStakeLimitExceeded(address pool, address delegator);
error InitialStakingPoolsListEmpty();
error InsufficientStakeAmount(address pool, address delegator);
error InvalidFixedEpochDuration();
error InvalidInitialStakeAmount(uint256 candidateStake, uint256 delegatorStake);
error InvalidIpAddressesCount();
error InvalidMaxStakeAmount();
error InvalidMoveStakePoolsAddress();
error InvalidOrderWithdrawAmount(address pool, address delegator, int256 amount);
error InvalidPublicKeysCount();
error InvalidStakingTransitionTimeframe();
error InvalidStakingFixedEpochDuration();
error InvalidTransitionTimeFrame();
error InvalidWithdrawAmount(address pool, address delegator, uint256 amount);
error InvalidNodeOperatorConfiguration(address _operator, uint256 _share);
error InvalidNodeOperatorShare(uint256 _share);
error WithdrawNotAllowed();
error ZeroWidthrawAmount();
error ZeroWidthrawDisallowPeriod();
// ============================================== Modifiers =======================================================
/// @dev Ensures the transaction gas price is not zero.
modifier gasPriceIsValid() {
if (tx.gasprice == 0) {
revert ZeroGasPrice();
}
_;
}
/// @dev Ensures the caller is the ValidatorSetHbbft contract address.
modifier onlyValidatorSetContract() virtual {
if (msg.sender != address(validatorSetContract)) {
revert Unauthorized();
}
_;
}
modifier onlyBlockRewardContract() {
if (msg.sender != validatorSetContract.blockRewardContract()) {
revert Unauthorized();
}
_;
}
modifier onlyBonusScoreContract() {
if (msg.sender != address(bonusScoreContract)) {
revert Unauthorized();
}
_;
}
// =============================================== Setters ========================================================
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
// Prevents initialization of implementation contract
_disableInitializers();
}
/// @dev Fallback function. Prevents direct sending native coins to this contract.
receive() external payable {
revert NotPayable();
}
/// @dev Initializes the network parameters.
/// Can only be called by the constructor of the `InitializerHbbft` contract or owner.
/// @param _contractOwner The address of the contract owner
/// @param stakingParams stores other parameters due to stack too deep issue
/// _validatorSetContract The address of the `ValidatorSetHbbft` contract.
/// _initialStakingAddresses The array of initial validators' staking addresses.
/// _delegatorMinStake The minimum allowed amount of delegator stake in Wei.
/// _candidateMinStake The minimum allowed amount of candidate/validator stake in Wei.
/// _stakingFixedEpochDuration The fixed duration of each epoch before keyGen starts.
/// _stakingTransitionTimeframeLength Length of the timeframe in seconds for the transition
/// to the new validator set.
/// _stakingWithdrawDisallowPeriod The duration period at the end of a staking epoch
/// during which participants cannot stake/withdraw/order/claim their staking coins
function initialize(
address _contractOwner,
StakingParams calldata stakingParams,
bytes32[] calldata _publicKeys,
bytes16[] calldata _internetAddresses
) external initializer {
if (_contractOwner == address(0)) {
revert ZeroAddress();
}
_validateStakingParams(stakingParams);
if (stakingParams._initialStakingAddresses.length * 2 != _publicKeys.length) {
revert InvalidPublicKeysCount();
}
if (stakingParams._initialStakingAddresses.length != _internetAddresses.length) {
revert InvalidIpAddressesCount();
}
__Ownable_init(_contractOwner);
__ReentrancyGuard_init();
validatorSetContract = IValidatorSetHbbft(stakingParams._validatorSetContract);
bonusScoreContract = IBonusScoreSystem(stakingParams._bonusScoreContract);
address[] calldata initStakingAddresses = stakingParams._initialStakingAddresses;
for (uint256 i = 0; i < initStakingAddresses.length; ++i) {
if (initStakingAddresses[i] == address(0)) {
revert ZeroAddress();
}
_addPoolActive(initStakingAddresses[i], false);
_addPoolToBeRemoved(initStakingAddresses[i]);
poolInfo[initStakingAddresses[i]].publicKey = abi.encodePacked(_publicKeys[i * 2], _publicKeys[i * 2 + 1]);
poolInfo[initStakingAddresses[i]].internetAddress = _internetAddresses[i];
}
uint256[] memory delegatorMinStakeAllowedParams = new uint256[](5);
delegatorMinStakeAllowedParams[0] = 50 ether;
delegatorMinStakeAllowedParams[1] = 100 ether;
delegatorMinStakeAllowedParams[2] = 150 ether;
delegatorMinStakeAllowedParams[3] = 200 ether;
delegatorMinStakeAllowedParams[4] = 250 ether;
__initAllowedChangeableParameter(
this.setDelegatorMinStake.selector,
this.delegatorMinStake.selector,
delegatorMinStakeAllowedParams
);
delegatorMinStake = stakingParams._delegatorMinStake;
candidateMinStake = stakingParams._candidateMinStake;
maxStakeAmount = stakingParams._maxStake;
stakingFixedEpochDuration = stakingParams._stakingFixedEpochDuration;
stakingWithdrawDisallowPeriod = stakingParams._stakingWithdrawDisallowPeriod;
// note: this might be still 0 when created in the genesis block.
stakingEpochStartTime = block.timestamp;
stakingTransitionTimeframeLength = stakingParams._stakingTransitionTimeframeLength;
}
/**
* @dev Sets the minimum stake required for delegators.
* @param _minStake The new minimum stake amount.
* Requirements:
* - Only the contract owner can call this function.
* - The stake amount must be within the allowed range.
*
* Emits a {SetDelegatorMinStake} event.
*/
function setDelegatorMinStake(uint256 _minStake) external onlyOwner withinAllowedRange(_minStake) {
delegatorMinStake = _minStake;
emit SetDelegatorMinStake(_minStake);
}
/// @dev Sets the timetamp of the current epoch's last block as the start time of the upcoming staking epoch.
/// Called by the `ValidatorSetHbbft.newValidatorSet` function at the last block of a staking epoch.
/// @param _timestamp The starting time of the very first block in the upcoming staking epoch.
function setStakingEpochStartTime(uint256 _timestamp) external onlyValidatorSetContract {
stakingEpochStartTime = _timestamp;
stakingEpochStartBlock = block.number;
}
/// @dev set's the validators ip address.
/// this function can only be called by the validator Set contract.
/// @param _validatorAddress address if the validator. (mining address)
/// @param _ip IPV4 address of a running Node Software or Proxy.
function setValidatorInternetAddress(
address _validatorAddress,
bytes16 _ip,
bytes2 _port
) external onlyValidatorSetContract {
poolInfo[_validatorAddress].internetAddress = _ip;
poolInfo[_validatorAddress].port = _port;
}
/// @dev Increments the serial number of the current staking epoch.
/// Called by the `ValidatorSetHbbft.newValidatorSet` at the last block of the finished staking epoch.
function incrementStakingEpoch() external onlyValidatorSetContract {
stakingEpoch++;
currentKeyGenExtraTimeWindow = 0;
}
/// @dev Notifies hbbft staking contract that the
/// key generation has failed, and a new round
/// of keygeneration starts.
function notifyKeyGenFailed() public onlyValidatorSetContract {
// we allow a extra time window for the current key generation
// equal in the size of the usual transition timeframe.
currentKeyGenExtraTimeWindow += stakingTransitionTimeframeLength;
}
/// @dev Notifies hbbft staking contract about a detected
/// network offline time.
/// if there is no handling for this,
/// validators got chosen outside the transition timewindow
/// and get banned immediatly, since they never got their chance
/// to write their keys.
/// more about: https://github.com/DMDcoin/hbbft-posdao-contracts/issues/96
function notifyNetworkOfftimeDetected(uint256 detectedOfflineTime) public onlyValidatorSetContract {
currentKeyGenExtraTimeWindow =
currentKeyGenExtraTimeWindow +
detectedOfflineTime +
stakingTransitionTimeframeLength;
}
/// @dev Notifies hbbft staking contract that a validator
/// asociated with the given `_stakingAddress` became
/// available again and can be put on to the list
/// of available nodes again.
function notifyAvailability(address _stakingAddress) public onlyValidatorSetContract {
if (stakeAmount[_stakingAddress][_stakingAddress] >= candidateMinStake) {
_addPoolActive(_stakingAddress, true);
_setLikelihood(_stakingAddress);
}
}
/// @dev Adds a new candidate's pool to the list of active pools (see the `getPools` getter) and
/// moves the specified amount of staking coins from the candidate's staking address
/// to the candidate's pool. A participant calls this function using their staking address when
/// they want to create a pool. This is a wrapper for the `stake` function.
/// @param _miningAddress The mining address of the candidate. The mining address is bound to the staking address
/// (msg.sender). This address cannot be equal to `msg.sender`.
/// @param _nodeOperatorAddress Address of node operator, will receive `_operatorShare` of epoch rewards.
/// @param _operatorShare Percent of epoch rewards to send to `_nodeOperatorAddress`.
/// Integer value with 2 decimal places, e.g. 1% = 100, 10.25% = 1025.
function addPool(
address _miningAddress,
address _nodeOperatorAddress,
uint256 _operatorShare,
bytes calldata _publicKey,
bytes16 _ip
) external payable gasPriceIsValid {
address stakingAddress = msg.sender;
uint256 amount = msg.value;
validatorSetContract.setStakingAddress(_miningAddress, stakingAddress);
// The staking address and the staker are the same.
poolInfo[stakingAddress].publicKey = _publicKey;
poolInfo[stakingAddress].internetAddress = _ip;
_setNodeOperator(stakingAddress, _nodeOperatorAddress, _operatorShare);
_stake(stakingAddress, stakingAddress, amount);
emit PlacedStake(stakingAddress, stakingAddress, stakingEpoch, amount);
}
/// @dev Removes the candidate's or validator's pool from the `pools` array (a list of active pools which
/// can be retrieved by the `getPools` getter). When a candidate or validator wants to remove their pool,
/// they should call this function from their staking address.
function removeMyPool() external gasPriceIsValid {
address stakingAddress = msg.sender;
address miningAddress = validatorSetContract.miningByStakingAddress(stakingAddress);
// initial validator cannot remove their pool during the initial staking epoch
if (stakingEpoch == 0 && validatorSetContract.isValidator(miningAddress)) {
revert PoolCannotBeRemoved(stakingAddress);
}
_removePool(stakingAddress);
}
/// @dev set's the pool info for a specific ethereum address.
/// @param _publicKey public key of the (future) signing address.
/// @param _ip (optional) IPV4 address of a running Node Software or Proxy.
/// @param _port (optional) port of IPv4 address of a running Node Software or Proxy.
/// Stores the supplied data for a staking (pool) address.
/// This function is external available without security checks,
/// since no array operations are used in the implementation,
/// this allows the flexibility to set the pool information before
/// adding the stake to the pool.
function setPoolInfo(bytes calldata _publicKey, bytes16 _ip, bytes2 _port) external {
poolInfo[msg.sender].publicKey = _publicKey;
poolInfo[msg.sender].internetAddress = _ip;
poolInfo[msg.sender].port = _port;
}
/// @dev Set's the pool node operator configuration for a specific ethereum address.
/// @param _operatorAddress Node operator address.
/// @param _operatorShare Node operator reward share percent.
function setNodeOperator(address _operatorAddress, uint256 _operatorShare) external {
if (validatorSetContract.miningByStakingAddress(msg.sender) == address(0)) {
revert PoolNotExist(msg.sender);
}
_setNodeOperator(msg.sender, _operatorAddress, _operatorShare);
}
/// @dev Removes a specified pool from the `pools` array (a list of active pools which can be retrieved by the
/// `getPools` getter). Called by the `ValidatorSetHbbft._removeMaliciousValidator` internal function,
/// and the `ValidatorSetHbbft.handleFailedKeyGeneration` function
/// when a pool must be removed by the algorithm.
/// @param _stakingAddress The staking address of the pool to be removed.
function removePool(address _stakingAddress) external onlyValidatorSetContract {
_removePool(_stakingAddress);
}
/// @dev Removes pools which are in the `_poolsToBeRemoved` internal array from the `pools` array.
/// Called by the `ValidatorSetHbbft.newValidatorSet` function when a pool must be removed by the algorithm.
function removePools() external onlyValidatorSetContract {
address[] memory poolsToRemove = _poolsToBeRemoved.values();
for (uint256 i = 0; i < poolsToRemove.length; i++) {
_removePool(poolsToRemove[i]);
}
}
/// @dev Moves the specified amount of staking coins from the staker's address to the staking address of
/// the specified pool. Actually, the amount is stored in a balance of this StakingHbbft contract.
/// A staker calls this function when they want to make a stake into a pool.
/// @param _toPoolStakingAddress The staking address of the pool where the coins should be staked.
function stake(address _toPoolStakingAddress) external payable gasPriceIsValid {
address staker = msg.sender;
uint256 amount = msg.value;
_stake(_toPoolStakingAddress, staker, amount);
emit PlacedStake(_toPoolStakingAddress, staker, stakingEpoch, amount);
}
/// @dev Moves the specified amount of staking coins from the staking address of
/// the specified pool to the staker's address. A staker calls this function when they want to withdraw
/// their coins.
/// @param _fromPoolStakingAddress The staking address of the pool from which the coins should be withdrawn.
/// @param _amount The amount of coins to be withdrawn. The amount cannot exceed the value returned
/// by the `maxWithdrawAllowed` getter.
function withdraw(address _fromPoolStakingAddress, uint256 _amount) external gasPriceIsValid nonReentrant {
address payable staker = payable(msg.sender);
_withdraw(_fromPoolStakingAddress, staker, _amount);
TransferUtils.transferNative(staker, _amount);
emit WithdrewStake(_fromPoolStakingAddress, staker, stakingEpoch, _amount);
}
/// @dev Moves staking coins from one pool to another. A staker calls this function when they want
/// to move their coins from one pool to another without withdrawing their coins.
/// @param _fromPoolStakingAddress The staking address of the source pool.
/// @param _toPoolStakingAddress The staking address of the target pool.
/// @param _amount The amount of staking coins to be moved. The amount cannot exceed the value returned
/// by the `maxWithdrawAllowed` getter.
function moveStake(
address _fromPoolStakingAddress,
address _toPoolStakingAddress,
uint256 _amount
) external gasPriceIsValid {
if (_fromPoolStakingAddress == _toPoolStakingAddress) {
revert InvalidMoveStakePoolsAddress();
}
address staker = msg.sender;
_withdraw(_fromPoolStakingAddress, staker, _amount);
_stake(_toPoolStakingAddress, staker, _amount);
emit MovedStake(_fromPoolStakingAddress, _toPoolStakingAddress, staker, stakingEpoch, _amount);
}
function restake(
address _poolStakingAddress,
uint256 _validatorMinRewardPercent
) external payable onlyBlockRewardContract {
// msg.value is a pool reward
if (msg.value == 0) {
return;
}
uint256 poolReward = msg.value;
uint256 totalStake = snapshotPoolTotalStakeAmount[stakingEpoch][_poolStakingAddress];
PoolRewardShares memory shares = _splitPoolReward(_poolStakingAddress, poolReward, _validatorMinRewardPercent);
address[] memory delegators = poolDelegators(_poolStakingAddress);
for (uint256 i = 0; i < delegators.length; ++i) {
uint256 delegatorReward = (shares.delegatorsShare *
_getDelegatorStake(stakingEpoch, _poolStakingAddress, delegators[i])) / totalStake;
stakeAmount[_poolStakingAddress][delegators[i]] += delegatorReward;
_stakeAmountByEpoch[_poolStakingAddress][delegators[i]][stakingEpoch] += delegatorReward;
}
if (shares.nodeOperatorShare != 0) {
_rewardNodeOperator(_poolStakingAddress, shares.nodeOperatorShare);
}
stakeAmount[_poolStakingAddress][_poolStakingAddress] += shares.validatorShare;
stakeAmountTotal[_poolStakingAddress] += poolReward;
totalStakedAmount += poolReward;
_setLikelihood(_poolStakingAddress);
emit RestakeReward(
_poolStakingAddress,
stakingEpoch,
shares.validatorShare,
poolReward - shares.validatorShare
);
}
/// @dev Orders coins withdrawal from the staking address of the specified pool to the
/// staker's address. The requested coins can be claimed after the current staking epoch is complete using
/// the `claimOrderedWithdraw` function.
/// @param _poolStakingAddress The staking address of the pool from which the amount will be withdrawn.
/// @param _amount The amount to be withdrawn. A positive value means the staker wants to either set or
/// increase their withdrawal amount. A negative value means the staker wants to decrease a
/// withdrawal amount that was previously set. The amount cannot exceed the value returned by the
/// `maxWithdrawOrderAllowed` getter.
function orderWithdraw(address _poolStakingAddress, int256 _amount) external gasPriceIsValid {
if (_poolStakingAddress == address(0)) {
revert ZeroAddress();
}
if (_amount == 0) {
revert ZeroWidthrawAmount();
}
address staker = msg.sender;
if (!areStakeAndWithdrawAllowed()) {
revert WithdrawNotAllowed();
}
uint256 newOrderedAmount = orderedWithdrawAmount[_poolStakingAddress][staker];
uint256 newOrderedAmountTotal = orderedWithdrawAmountTotal[_poolStakingAddress];
uint256 newStakeAmount = stakeAmount[_poolStakingAddress][staker];
uint256 newStakeAmountTotal = stakeAmountTotal[_poolStakingAddress];
if (_amount > 0) {
uint256 amount = uint256(_amount);
// How much can `staker` order for withdrawal from `_poolStakingAddress` at the moment?
uint256 allowedWithdraw = maxWithdrawOrderAllowed(_poolStakingAddress, staker);
if (amount > allowedWithdraw) {
revert MaxAllowedWithdrawExceeded(allowedWithdraw, amount);
}
newOrderedAmount = newOrderedAmount + amount;
newOrderedAmountTotal = newOrderedAmountTotal + amount;
newStakeAmount = newStakeAmount - amount;
newStakeAmountTotal = newStakeAmountTotal - amount;
totalStakedAmount -= amount;
orderWithdrawEpoch[_poolStakingAddress][staker] = stakingEpoch;
} else {
uint256 amount = uint256(-_amount);
newOrderedAmount = newOrderedAmount - amount;
newOrderedAmountTotal = newOrderedAmountTotal - amount;
newStakeAmount = newStakeAmount + amount;
newStakeAmountTotal = newStakeAmountTotal + amount;
totalStakedAmount += amount;
}
orderedWithdrawAmount[_poolStakingAddress][staker] = newOrderedAmount;
orderedWithdrawAmountTotal[_poolStakingAddress] = newOrderedAmountTotal;
stakeAmount[_poolStakingAddress][staker] = newStakeAmount;
stakeAmountTotal[_poolStakingAddress] = newStakeAmountTotal;
if (staker == _poolStakingAddress) {
// The amount to be withdrawn must be the whole staked amount or
// must not exceed the diff between the entire amount and `candidateMinStake`
if (newStakeAmount != 0 && newStakeAmount < candidateMinStake) {
revert InvalidOrderWithdrawAmount(_poolStakingAddress, staker, _amount);
}
if (_amount > 0) {
// if the validator orders the `_amount` for withdrawal
if (newStakeAmount == 0) {
// If the validator orders their entire stake,
// mark their pool as `to be removed`
_addPoolToBeRemoved(_poolStakingAddress);
}
} else {
// If the validator wants to reduce withdrawal value,
// add their pool as `active` if it hasn't been already done.
_addPoolActive(_poolStakingAddress, true);
}
} else {
// The amount to be withdrawn must be the whole staked amount or
// must not exceed the diff between the entire amount and `delegatorMinStake`
if (newStakeAmount != 0 && newStakeAmount < delegatorMinStake) {
revert InvalidOrderWithdrawAmount(_poolStakingAddress, staker, _amount);
}
if (_amount > 0) {
// if the delegator orders the `_amount` for withdrawal
if (newStakeAmount == 0) {
// If the delegator orders their entire stake,
// remove the delegator from delegator list of the pool
_removePoolDelegator(_poolStakingAddress, staker);
}
} else {
// If the delegator wants to reduce withdrawal value,
// add them to delegator list of the pool if it hasn't already done
_addPoolDelegator(_poolStakingAddress, staker);
}
// Remember stake movement to use it later in the `claimReward` function
// _snapshotDelegatorStake(_poolStakingAddress, staker);
}
_setLikelihood(_poolStakingAddress);
emit OrderedWithdrawal(_poolStakingAddress, staker, stakingEpoch, _amount);
}
/// @dev Withdraws the staking coins from the specified pool ordered during the previous staking epochs with
/// the `orderWithdraw` function. The ordered amount can be retrieved by the `orderedWithdrawAmount` getter.
/// @param _poolStakingAddress The staking address of the pool from which the ordered coins are withdrawn.
function claimOrderedWithdraw(address _poolStakingAddress) external gasPriceIsValid nonReentrant {
address payable staker = payable(msg.sender);
if (stakingEpoch <= orderWithdrawEpoch[_poolStakingAddress][staker]) {
revert CannotClaimWithdrawOrderYet(_poolStakingAddress, staker);
}
if (!areStakeAndWithdrawAllowed()) {
revert WithdrawNotAllowed();
}
uint256 claimAmount = orderedWithdrawAmount[_poolStakingAddress][staker];
if (claimAmount == 0) {
revert ZeroWidthrawAmount();
}
orderedWithdrawAmount[_poolStakingAddress][staker] = 0;
orderedWithdrawAmountTotal[_poolStakingAddress] = orderedWithdrawAmountTotal[_poolStakingAddress] - claimAmount;
if (stakeAmount[_poolStakingAddress][staker] == 0) {
_withdrawCheckPool(_poolStakingAddress, staker);
}
TransferUtils.transferNative(staker, claimAmount);
emit ClaimedOrderedWithdrawal(_poolStakingAddress, staker, stakingEpoch, claimAmount);
}
/// @dev Distribute abandoned stakes among Reinsert and Governance pots.
/// 50% goes to reinsert and 50% to governance pot.
/// Coins are considered abandoned if they were staked on a validator inactive for 10 years.
function recoverAbandonedStakes() external gasPriceIsValid {
uint256 totalAbandonedAmount = 0;
address[] memory inactivePools = _poolsInactive.values();
if (inactivePools.length == 0) {
revert NoStakesToRecover();
}
for (uint256 i = 0; i < inactivePools.length; ++i) {
address stakingAddress = inactivePools[i];
if (_isPoolEmpty(stakingAddress) || !validatorSetContract.isValidatorAbandoned(stakingAddress)) {
continue;
}
_poolsInactive.remove(stakingAddress);
abandonedAndRemoved[stakingAddress] = true;
uint256 gatheredPerStakingAddress = stakeAmountTotal[stakingAddress];
stakeAmountTotal[stakingAddress] = 0;
totalStakedAmount -= gatheredPerStakingAddress;
address[] memory delegators = poolDelegators(stakingAddress);
for (uint256 j = 0; j < delegators.length; ++j) {
address delegator = delegators[j];
stakeAmount[stakingAddress][delegator] = 0;
_removePoolDelegator(stakingAddress, delegator);
}
totalAbandonedAmount += gatheredPerStakingAddress;
emit GatherAbandonedStakes(msg.sender, stakingAddress, gatheredPerStakingAddress);
}
if (totalAbandonedAmount == 0) {
revert NoStakesToRecover();
}
uint256 governanceShare = totalAbandonedAmount / 2;
uint256 reinsertShare = totalAbandonedAmount - governanceShare;
IBlockRewardHbbft blockRewardHbbft = IBlockRewardHbbft(validatorSetContract.blockRewardContract());
address governanceAddress = blockRewardHbbft.getGovernanceAddress();
// slither-disable-next-line arbitrary-send-eth
blockRewardHbbft.addToReinsertPot{ value: reinsertShare }();
TransferUtils.transferNative(governanceAddress, governanceShare);
emit RecoverAbandonedStakes(msg.sender, reinsertShare, governanceShare);
}
/// @dev Makes snapshots of total amount staked into the specified pool
/// before the specified staking epoch. Used by the `reward` function.
/// @param _epoch The number of upcoming staking epoch.
/// @param _stakingPool The staking address of the pool.
function snapshotPoolStakeAmounts(uint256 _epoch, address _stakingPool) external onlyBlockRewardContract {
if (snapshotPoolTotalStakeAmount[_epoch][_stakingPool] != 0) {
return;
}
uint256 totalAmount = stakeAmountTotal[_stakingPool];
if (totalAmount == 0) {
return;
}
snapshotPoolTotalStakeAmount[_epoch][_stakingPool] = totalAmount;
snapshotPoolValidatorStakeAmount[_epoch][_stakingPool] = stakeAmount[_stakingPool][_stakingPool];
}
function updatePoolLikelihood(address mining, uint256 validatorScore) external onlyBonusScoreContract {
address stakingAddress = validatorSetContract.stakingByMiningAddress(mining);
_updateLikelihood(stakingAddress, validatorScore);
}
// =============================================== Getters ========================================================
/// @dev Returns an array of the current active pools (the staking addresses of candidates and validators).
/// The size of the array cannot exceed MAX_CANDIDATES. A pool can be added to this array with the `_addPoolActive`
/// internal function which is called by the `stake` or `orderWithdraw` function. A pool is considered active
/// if its address has at least the minimum stake and this stake is not ordered to be withdrawn.
function getPools() external view returns (address[] memory) {
return _pools.values();
}
/// @dev Return the Public Key used by a Node to send targeted HBBFT Consensus Messages.
/// @param _poolAddress The Pool Address to query the public key for.
/// @return the public key for the given pool address.
/// Note that the public key does not convert to the ethereum address of the pool address.
/// The pool address is used for stacking, and not for signing HBBFT messages.
function getPoolPublicKey(address _poolAddress) external view returns (bytes memory) {
return poolInfo[_poolAddress].publicKey;
}
/// @dev Returns the registered IPv4 Address for the node.
/// @param _poolAddress The Pool Address to query the IPv4Address for.
/// @return IPv4 Address for the given pool address.
function getPoolInternetAddress(address _poolAddress) external view returns (bytes16, bytes2) {
return (poolInfo[_poolAddress].internetAddress, poolInfo[_poolAddress].port);
}
/// @dev Returns an array of the current inactive pools (the staking addresses of former candidates).
/// A pool can be added to this array with the `_addPoolInactive` internal function which is called
/// by `_removePool`. A pool is considered inactive if it is banned for some reason, if its address
/// has zero stake, or if its entire stake is ordered to be withdrawn.
function getPoolsInactive() external view returns (address[] memory) {
return _poolsInactive.values();
}
/// @dev Returns the array of stake amounts for each corresponding
/// address in the `poolsToBeElected` array (see the `getPoolsToBeElected` getter) and a sum of these amounts.
/// Used by the `ValidatorSetHbbft.newValidatorSet` function when randomly selecting new validators at the last
/// block of a staking epoch. An array value is updated every time any staked amount is changed in this pool
/// (see the `_setLikelihood` internal function).
/// @return likelihoods `uint256[] likelihoods` - The array of the coefficients. The array length is always equal
/// to the length of the `poolsToBeElected` array.
/// `uint256 sum` - The total sum of the amounts.
function getPoolsLikelihood() external view returns (uint256[] memory likelihoods, uint256 sum) {
return (_poolsLikelihood, _poolsLikelihoodSum);
}
/// @dev Returns the list of pools (their staking addresses) which will participate in a new validator set
/// selection process in the `ValidatorSetHbbft.newValidatorSet` function. This is an array of pools
/// which will be considered as candidates when forming a new validator set (at the last block of a staking epoch).
/// This array is kept updated by the `_addPoolToBeElected` and `_deletePoolToBeElected` internal functions.
function getPoolsToBeElected() external view returns (address[] memory) {
return _poolsToBeElected;
}
/// @dev Returns the list of pools (their staking addresses) which will be removed by the
/// `ValidatorSetHbbft.newValidatorSet` function from the active `pools` array (at the last block
/// of a staking epoch). This array is kept updated by the `_addPoolToBeRemoved`
/// and `_deletePoolToBeRemoved` internal functions. A pool is added to this array when the pool's
/// address withdraws (or orders) all of its own staking coins from the pool, inactivating the pool.
function getPoolsToBeRemoved() external view returns (address[] memory) {
return _poolsToBeRemoved.values();
}
function getPoolValidatorStakeAmount(uint256 _epoch, address _stakingPool) external view returns (uint256) {
return snapshotPoolValidatorStakeAmount[_epoch][_stakingPool];
}
/// @dev Determines whether staking/withdrawal operations are allowed at the moment.
/// Used by all staking/withdrawal functions.
function areStakeAndWithdrawAllowed() public pure returns (bool) {
//experimental change to always allow to stake withdraw.
//see https://github.com/DMDcoin/hbbft-posdao-contracts/issues/14 for discussion.
return true;
// used for testing
// if (stakingFixedEpochDuration == 0){
// return true;
// }
// uint256 currentTimestamp = block.timestamp;
// uint256 allowedDuration = stakingFixedEpochDuration - stakingWithdrawDisallowPeriod;
// return currentTimestamp - stakingEpochStartTime > allowedDuration; //TODO: should be < not <=?
}
/// @dev Returns a flag indicating whether a specified address is in the `pools` array.
/// See the `getPools` getter.
/// @param _stakingAddress The staking address of the pool.
function isPoolActive(address _stakingAddress) public view returns (bool) {
return _pools.contains(_stakingAddress);
}
/// @dev Returns a flag indicating whether a specified address is in the `_pools` or `poolsInactive` array.
/// @param _stakingAddress The staking address of the pool.
function isPoolValid(address _stakingAddress) public view returns (bool) {
return _pools.contains(_stakingAddress) || _poolsInactive.contains(_stakingAddress);
}
/// @dev Returns the maximum amount which can be withdrawn from the specified pool by the specified staker
/// at the moment. Used by the `withdraw` and `moveStake` functions.
/// @param _poolStakingAddress The pool staking address from which the withdrawal will be made.
/// @param _staker The staker address that is going to withdraw.
function maxWithdrawAllowed(address _poolStakingAddress, address _staker) public view returns (uint256) {
address miningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
if (!areStakeAndWithdrawAllowed() || abandonedAndRemoved[_poolStakingAddress]) {
return 0;
}
uint256 canWithdraw = stakeAmount[_poolStakingAddress][_staker];
if (!validatorSetContract.isValidatorOrPending(miningAddress)) {
// The pool is not a validator and is not going to become one,
// so the staker can only withdraw staked amount minus already
// ordered amount
return canWithdraw;
}
// The pool is a validator (active or pending), so the staker can only
// withdraw staked amount minus already ordered amount but
// no more than the amount staked during the current staking epoch
uint256 stakedDuringEpoch = stakeAmountByCurrentEpoch(_poolStakingAddress, _staker);
if (canWithdraw > stakedDuringEpoch) {
canWithdraw = stakedDuringEpoch;
}
return canWithdraw;
}
/// @dev Returns the maximum amount which can be ordered to be withdrawn from the specified pool by the
/// specified staker at the moment. Used by the `orderWithdraw` function.
/// @param _poolStakingAddress The pool staking address from which the withdrawal will be ordered.
/// @param _staker The staker address that is going to order the withdrawal.
function maxWithdrawOrderAllowed(address _poolStakingAddress, address _staker) public view returns (uint256) {
address miningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
if (!areStakeAndWithdrawAllowed()) {
return 0;
}
if (!validatorSetContract.isValidatorOrPending(miningAddress)) {
// If the pool is a candidate (not an active validator and not pending one),
// no one can order withdrawal from the `_poolStakingAddress`, but
// anyone can withdraw immediately (see the `maxWithdrawAllowed` getter)
return 0;
}
// If the pool is an active or pending validator, the staker can order withdrawal
// up to their total staking amount minus an already ordered amount
// minus an amount staked during the current staking epoch
return stakeAmount[_poolStakingAddress][_staker] - stakeAmountByCurrentEpoch(_poolStakingAddress, _staker);
}
/// @dev Returns an array of the current active delegators of the specified pool.
/// A delegator is considered active if they have staked into the specified
/// pool and their stake is not ordered to be withdrawn.
/// @param _poolStakingAddress The pool staking address.
function poolDelegators(address _poolStakingAddress) public view returns (address[] memory) {
return _poolDelegators[_poolStakingAddress].values();
}
/// @dev Returns an array of the current inactive delegators of the specified pool.
/// A delegator is considered inactive if their entire stake is ordered to be withdrawn
/// but not yet claimed.
/// @param _poolStakingAddress The pool staking address.
function poolDelegatorsInactive(address _poolStakingAddress) external view returns (address[] memory) {
return _poolDelegatorsInactive[_poolStakingAddress].values();
}
/// @dev Returns the amount of staking coins staked into the specified pool by the specified staker
/// during the current staking epoch (see the `stakingEpoch` getter).
/// Used by the `stake`, `withdraw`, and `orderWithdraw` functions.
/// @param _poolStakingAddress The pool staking address.
/// @param _staker The staker's address.
function stakeAmountByCurrentEpoch(address _poolStakingAddress, address _staker) public view returns (uint256) {
return _stakeAmountByEpoch[_poolStakingAddress][_staker][stakingEpoch];
}
/// @dev indicates the time when the new validatorset for the next epoch gets chosen.
/// this is the start of a timeframe before the end of the epoch,
/// that is long enough for the validators
/// to create a new shared key.
function startTimeOfNextPhaseTransition() public view returns (uint256) {
return stakingEpochStartTime + stakingFixedEpochDuration - stakingTransitionTimeframeLength;
}
/// @dev Returns an indicative time of the last block of the current staking epoch before key generation starts.
function stakingFixedEpochEndTime() public view returns (uint256) {
uint256 startTime = stakingEpochStartTime;
return
startTime +
stakingFixedEpochDuration +
currentKeyGenExtraTimeWindow -
(stakingFixedEpochDuration == 0 ? 0 : 1);
}
/// @dev Adds the specified staking address to the array of active pools returned by
/// the `getPools` getter. Used by the `stake`, `addPool`, and `orderWithdraw` functions.
/// @param _stakingAddress The pool added to the array of active pools.
/// @param _toBeElected The boolean flag which defines whether the specified address should be
/// added simultaneously to the `poolsToBeElected` array. See the `getPoolsToBeElected` getter.
function _addPoolActive(address _stakingAddress, bool _toBeElected) internal {
if (!isPoolActive(_stakingAddress)) {
_pools.add(_stakingAddress);
if (_pools.length() > _getMaxCandidates()) {
revert MaxPoolsCountExceeded();
}
}
_poolsInactive.remove(_stakingAddress);
if (_toBeElected) {
_addPoolToBeElected(_stakingAddress);
}
}
/// @dev Adds the specified staking address to the array of inactive pools returned by
/// the `getPoolsInactive` getter. Used by the `_removePool` internal function.
/// @param _stakingAddress The pool added to the array of inactive pools.
function _addPoolInactive(address _stakingAddress) internal {
// This function performs internal checks if value already exists
_poolsInactive.add(_stakingAddress);
}
/// @dev Adds the specified staking address to the array of pools returned by the `getPoolsToBeElected`
/// getter. Used by the `_addPoolActive` internal function. See the `getPoolsToBeElected` getter.
/// @param _stakingAddress The pool added to the `poolsToBeElected` array.
function _addPoolToBeElected(address _stakingAddress) private {
uint256 index = poolToBeElectedIndex[_stakingAddress];
uint256 length = _poolsToBeElected.length;
if (index >= length || _poolsToBeElected[index] != _stakingAddress) {
poolToBeElectedIndex[_stakingAddress] = length;
_poolsToBeElected.push(_stakingAddress);
_poolsLikelihood.push(0); // assumes the likelihood is set with `_setLikelihood` function hereinafter
}
_deletePoolToBeRemoved(_stakingAddress);
}
/// @dev Adds the specified staking address to the array of pools returned by the `getPoolsToBeRemoved`
/// getter. Used by withdrawal functions. See the `getPoolsToBeRemoved` getter.
/// @param _stakingAddress The pool added to the `poolsToBeRemoved` array.
function _addPoolToBeRemoved(address _stakingAddress) private {
_poolsToBeRemoved.add(_stakingAddress);
_deletePoolToBeElected(_stakingAddress);
}
/// @dev Deletes the specified staking address from the array of pools returned by the
/// `getPoolsToBeElected` getter. Used by the `_addPoolToBeRemoved` and `_removePool` internal functions.
/// See the `getPoolsToBeElected` getter.
/// @param _stakingAddress The pool deleted from the `poolsToBeElected` array.
function _deletePoolToBeElected(address _stakingAddress) private {
if (_poolsToBeElected.length != _poolsLikelihood.length) return;
uint256 indexToDelete = poolToBeElectedIndex[_stakingAddress];
if (_poolsToBeElected.length > indexToDelete && _poolsToBeElected[indexToDelete] == _stakingAddress) {
if (_poolsLikelihoodSum >= _poolsLikelihood[indexToDelete]) {
_poolsLikelihoodSum -= _poolsLikelihood[indexToDelete];
} else {
_poolsLikelihoodSum = 0;
}
uint256 lastPoolIndex = _poolsToBeElected.length - 1;
address lastPool = _poolsToBeElected[lastPoolIndex];
_poolsToBeElected[indexToDelete] = lastPool;
_poolsLikelihood[indexToDelete] = _poolsLikelihood[lastPoolIndex];
poolToBeElectedIndex[lastPool] = indexToDelete;
poolToBeElectedIndex[_stakingAddress] = 0;
_poolsToBeElected.pop();
_poolsLikelihood.pop();
}
}
/// @dev Deletes the specified staking address from the array of pools returned by the
/// `getPoolsToBeRemoved` getter. Used by the `_addPoolToBeElected` and `_removePool` internal functions.
/// See the `getPoolsToBeRemoved` getter.
/// @param _stakingAddress The pool deleted from the `poolsToBeRemoved` array.
function _deletePoolToBeRemoved(address _stakingAddress) private {
_poolsToBeRemoved.remove(_stakingAddress);
}
/// @dev Removes the specified staking address from the array of active pools returned by
/// the `getPools` getter. Used by the `removePool`, `removeMyPool`, and withdrawal functions.
/// @param _stakingAddress The pool removed from the array of active pools.
function _removePool(address _stakingAddress) private {
// This function performs existence check internally
_pools.remove(_stakingAddress);
if (_isPoolEmpty(_stakingAddress)) {
_poolsInactive.remove(_stakingAddress);
} else {
_addPoolInactive(_stakingAddress);
}
_deletePoolToBeElected(_stakingAddress);
_deletePoolToBeRemoved(_stakingAddress);
}
function _validateStakingParams(StakingParams calldata params) private pure {
if (
params._stakingFixedEpochDuration == 0 ||
params._stakingFixedEpochDuration <= params._stakingWithdrawDisallowPeriod
) {
revert InvalidFixedEpochDuration();
}
if (params._stakingWithdrawDisallowPeriod == 0) {
revert ZeroWidthrawDisallowPeriod();
}
if (
params._stakingTransitionTimeframeLength == 0 ||
params._stakingTransitionTimeframeLength >= params._stakingFixedEpochDuration
) {
revert InvalidTransitionTimeFrame();
}
if (params._validatorSetContract == address(0)) {
revert ZeroAddress();
}
if (params._initialStakingAddresses.length == 0) {
revert InitialStakingPoolsListEmpty();
}
if (params._delegatorMinStake == 0 || params._candidateMinStake == 0) {
revert InvalidInitialStakeAmount(params._candidateMinStake, params._delegatorMinStake);
}
if (params._maxStake <= params._candidateMinStake) {
revert InvalidMaxStakeAmount();
}
}
/// @dev Returns the max number of candidates (including validators). See the MAX_CANDIDATES constant.
/// Needed mostly for unit tests.
function _getMaxCandidates() internal pure virtual returns (uint256) {
return MAX_CANDIDATES;
}
/// @dev Adds the specified address to the array of the current active delegators of the specified pool.
/// Used by the `stake` and `orderWithdraw` functions. See the `poolDelegators` getter.
/// @param _poolStakingAddress The pool staking address.
/// @param _delegator The delegator's address.
function _addPoolDelegator(address _poolStakingAddress, address _delegator) private {
_poolDelegators[_poolStakingAddress].add(_delegator);
_removePoolDelegatorInactive(_poolStakingAddress, _delegator);
}
/// @dev Adds the specified address to the array of the current inactive delegators of the specified pool.
/// Used by the `_removePoolDelegator` internal function.
/// @param _poolStakingAddress The pool staking address.
/// @param _delegator The delegator's address.
function _addPoolDelegatorInactive(address _poolStakingAddress, address _delegator) private {
_poolDelegatorsInactive[_poolStakingAddress].add(_delegator);
}
/// @dev Removes the specified address from the array of the current active delegators of the specified pool.
/// Used by the withdrawal functions. See the `poolDelegators` getter.
/// @param _poolStakingAddress The pool staking address.
/// @param _delegator The delegator's address.
function _removePoolDelegator(address _poolStakingAddress, address _delegator) private {
_poolDelegators[_poolStakingAddress].remove(_delegator);
if (orderedWithdrawAmount[_poolStakingAddress][_delegator] != 0) {
_addPoolDelegatorInactive(_poolStakingAddress, _delegator);
} else {
_removePoolDelegatorInactive(_poolStakingAddress, _delegator);
}
}
/// @dev Removes the specified address from the array of the inactive delegators of the specified pool.
/// Used by the `_addPoolDelegator` and `_removePoolDelegator` internal functions.
/// @param _poolStakingAddress The pool staking address.
/// @param _delegator The delegator's address.
function _removePoolDelegatorInactive(address _poolStakingAddress, address _delegator) private {
_poolDelegatorsInactive[_poolStakingAddress].remove(_delegator);
}
/// @dev Calculates (updates) the probability of being selected as a validator for the specified pool
/// and updates the total sum of probability coefficients. Actually, the probability is equal to the
/// amount totally staked into the pool multiplied by validator bonus score. See the `getPoolsLikelihood` getter.
/// Used by the staking and withdrawal functions.
/// @param _poolStakingAddress The address of the pool for which the probability coefficient must be updated.
function _setLikelihood(address _poolStakingAddress) private {
address miningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
uint256 validatorBonusScore = bonusScoreContract.getValidatorScore(miningAddress);
_updateLikelihood(_poolStakingAddress, validatorBonusScore);
}
function _updateLikelihood(address _poolStakingAddress, uint256 validatorBonusScore) private {
(bool isToBeElected, uint256 index) = _isPoolToBeElected(_poolStakingAddress);
if (!isToBeElected) return;
uint256 oldValue = _poolsLikelihood[index];
uint256 newValue = stakeAmountTotal[_poolStakingAddress] * validatorBonusScore;
_poolsLikelihood[index] = newValue;
_poolsLikelihoodSum = _poolsLikelihoodSum - oldValue + newValue;
}
/// @dev The internal function used by the `_stake` and `moveStake` functions.
/// See the `stake` public function for more details.
/// @param _poolStakingAddress The staking address of the pool where the coins should be staked.
/// @param _staker The staker's address.
/// @param _amount The amount of coins to be staked.
function _stake(address _poolStakingAddress, address _staker, uint256 _amount) private {
if (_poolStakingAddress == address(0)) {
revert ZeroAddress();
}
address poolMiningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
if (poolMiningAddress == address(0)) {
revert PoolNotExist(_poolStakingAddress);
}
if (_amount == 0) {
revert InsufficientStakeAmount(_poolStakingAddress, _staker);
}
if (abandonedAndRemoved[_poolStakingAddress]) {
revert PoolAbandoned(_poolStakingAddress);
}
//require(areStakeAndWithdrawAllowed(), "Stake: disallowed period");
bool selfStake = _staker == _poolStakingAddress;
uint256 newStakeAmount = stakeAmount[_poolStakingAddress][_staker] + _amount;
uint256 requiredStakeAmount;
if (selfStake) {
requiredStakeAmount = candidateMinStake;
} else {
requiredStakeAmount = delegatorMinStake;
// The delegator cannot stake into the pool of the candidate which hasn't self-staked.
// Also, that candidate shouldn't want to withdraw all their funds.
if (stakeAmount[_poolStakingAddress][_poolStakingAddress] == 0) {
revert PoolEmpty(_poolStakingAddress);
}
}
if (newStakeAmount < requiredStakeAmount) {
revert InsufficientStakeAmount(_poolStakingAddress, _staker);
}
if (stakeAmountTotal[_poolStakingAddress] + _amount > maxStakeAmount) {
revert PoolStakeLimitExceeded(_poolStakingAddress, _staker);
}
_stakeAmountByEpoch[_poolStakingAddress][_staker][stakingEpoch] += _amount;
stakeAmountTotal[_poolStakingAddress] += _amount;
totalStakedAmount += _amount;
if (selfStake) {
// `staker` places a stake for himself and becomes a candidate
// Add `_poolStakingAddress` to the array of pools
_addPoolActive(_poolStakingAddress, true);
} else {
// Add `_staker` to the array of pool's delegators
_addPoolDelegator(_poolStakingAddress, _staker);
// Save amount value staked by the delegator
_snapshotDelegatorStake(_poolStakingAddress, poolMiningAddress, _staker);
}
stakeAmount[_poolStakingAddress][_staker] = newStakeAmount;
_setLikelihood(_poolStakingAddress);
}
/// @dev The internal function used by the `withdraw` and `moveStake` functions.
/// See the `withdraw` public function for more details.
/// @param _poolStakingAddress The staking address of the pool from which the coins should be withdrawn.
/// @param _staker The staker's address.
/// @param _amount The amount of coins to be withdrawn.
function _withdraw(address _poolStakingAddress, address _staker, uint256 _amount) private {
if (_poolStakingAddress == address(0)) {
revert ZeroAddress();
}
if (_amount == 0) {
revert ZeroWidthrawAmount();
}
// How much can `staker` withdraw from `_poolStakingAddress` at the moment?
uint256 allowedMaxWithdraw = maxWithdrawAllowed(_poolStakingAddress, _staker);
if (_amount > allowedMaxWithdraw) {
revert MaxAllowedWithdrawExceeded(allowedMaxWithdraw, _amount);
}
uint256 newStakeAmount = stakeAmount[_poolStakingAddress][_staker] - _amount;
// The amount to be withdrawn must be the whole staked amount or
// must not exceed the diff between the entire amount and MIN_STAKE
uint256 minAllowedStake = (_poolStakingAddress == _staker) ? candidateMinStake : delegatorMinStake;
if (newStakeAmount != 0 && newStakeAmount < minAllowedStake) {
revert InvalidWithdrawAmount(_poolStakingAddress, _staker, _amount);
}
if (_staker != _poolStakingAddress) {
address miningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
_snapshotDelegatorStake(_poolStakingAddress, miningAddress, _staker);
}
stakeAmount[_poolStakingAddress][_staker] = newStakeAmount;
uint256 amountByEpoch = stakeAmountByCurrentEpoch(_poolStakingAddress, _staker);
_stakeAmountByEpoch[_poolStakingAddress][_staker][stakingEpoch] = amountByEpoch >= _amount
? amountByEpoch - _amount
: 0;
stakeAmountTotal[_poolStakingAddress] -= _amount;
totalStakedAmount -= _amount;
if (newStakeAmount == 0) {
_withdrawCheckPool(_poolStakingAddress, _staker);
}
_setLikelihood(_poolStakingAddress);
}
/// @dev The internal function used by the `_withdraw` and `claimOrderedWithdraw` functions.
/// Contains a common logic for these functions.
/// @param _poolStakingAddress The staking address of the pool from which the coins are withdrawn.
/// @param _staker The staker's address.
function _withdrawCheckPool(address _poolStakingAddress, address _staker) private {
if (_staker == _poolStakingAddress) {
address miningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
if (validatorSetContract.isValidator(miningAddress)) {
_addPoolToBeRemoved(_poolStakingAddress);
} else {
_removePool(_poolStakingAddress);
}
} else {
_removePoolDelegator(_poolStakingAddress, _staker);
if (_isPoolEmpty(_poolStakingAddress)) {
_poolsInactive.remove(_poolStakingAddress);
}
}
}
function _snapshotDelegatorStake(address _stakingAddress, address _miningAddress, address _delegator) private {
if (!validatorSetContract.isValidatorOrPending(_miningAddress) || stakingEpoch == 0) {
return;
}
uint256 lastSnapshotEpochNumber = _stakeSnapshotLastEpoch[_stakingAddress][_delegator];
if (lastSnapshotEpochNumber < stakingEpoch) {
_delegatorStakeSnapshot[_stakingAddress][_delegator][stakingEpoch] = stakeAmount[_stakingAddress][
_delegator
];
_stakeSnapshotLastEpoch[_stakingAddress][_delegator] = stakingEpoch;
}
}
function _setNodeOperator(
address _stakingAddress,
address _operatorAddress,
uint256 _operatorSharePercent
) private {
if (_operatorSharePercent > MAX_NODE_OPERATOR_SHARE_PERCENT) {
revert InvalidNodeOperatorShare(_operatorSharePercent);
}
if (_operatorAddress == address(0) && _operatorSharePercent != 0) {
revert InvalidNodeOperatorConfiguration(_operatorAddress, _operatorSharePercent);
}
uint256 lastChangeEpoch = poolNodeOperatorLastChangeEpoch[_stakingAddress];
if (lastChangeEpoch != 0 && lastChangeEpoch == stakingEpoch) {
revert OnlyOncePerEpoch(stakingEpoch);
}
poolNodeOperator[_stakingAddress] = _operatorAddress;
poolNodeOperatorShare[_stakingAddress] = _operatorSharePercent;
poolNodeOperatorLastChangeEpoch[_stakingAddress] = stakingEpoch;
emit SetNodeOperator(_stakingAddress, _operatorAddress, _operatorSharePercent);
}
function _rewardNodeOperator(address _stakingAddress, uint256 _operatorShare) private {
address nodeOperator = poolNodeOperator[_stakingAddress];
if (!_poolDelegators[_stakingAddress].contains(nodeOperator)) {
_addPoolDelegator(_stakingAddress, nodeOperator);
}
stakeAmount[_stakingAddress][nodeOperator] += _operatorShare;
_stakeAmountByEpoch[_stakingAddress][nodeOperator][stakingEpoch] += _operatorShare;
}
function _getDelegatorStake(
uint256 _stakingEpoch,
address _stakingAddress,
address _delegator
) private view returns (uint256) {
if (_stakingEpoch == 0) {
return 0;
}
if (_stakeSnapshotLastEpoch[_stakingAddress][_delegator] == _stakingEpoch) {
return _delegatorStakeSnapshot[_stakingAddress][_delegator][_stakingEpoch];
} else {
return stakeAmount[_stakingAddress][_delegator];
}
}
/// @dev Returns a boolean flag indicating whether the specified pool is fully empty
/// (all stakes are withdrawn including ordered withdrawals).
/// @param _poolStakingAddress The staking address of the pool
function _isPoolEmpty(address _poolStakingAddress) private view returns (bool) {
return stakeAmountTotal[_poolStakingAddress] == 0 && orderedWithdrawAmountTotal[_poolStakingAddress] == 0;
}
/// @dev Determines if the specified pool is in the `poolsToBeElected` array. See the `getPoolsToBeElected` getter.
/// Used by the `_setLikelihood` internal function.
/// @param _stakingAddress The staking address of the pool.
/// @return toBeElected `bool toBeElected` - The boolean flag indicating whether the `_stakingAddress` is in the
/// `poolsToBeElected` array.
/// `uint256 index` - The position of the item in the `poolsToBeElected` array if `toBeElected` is `true`.
function _isPoolToBeElected(address _stakingAddress) private view returns (bool toBeElected, uint256 index) {
index = poolToBeElectedIndex[_stakingAddress];
if (_poolsToBeElected.length > index && _poolsToBeElected[index] == _stakingAddress) {
return (true, index);
}
return (false, 0);
}
function _splitPoolReward(
address _poolAddress,
uint256 _poolReward,
uint256 _validatorMinRewardPercent
) private view returns (PoolRewardShares memory shares) {
uint256 totalStake = snapshotPoolTotalStakeAmount[stakingEpoch][_poolAddress];
uint256 validatorStake = snapshotPoolValidatorStakeAmount[stakingEpoch][_poolAddress];
uint256 validatorFixedReward = (_poolReward * _validatorMinRewardPercent) / 100;
shares.delegatorsShare = _poolReward - validatorFixedReward;
uint256 operatorSharePercent = poolNodeOperatorShare[_poolAddress];
if (poolNodeOperator[_poolAddress] != address(0) && operatorSharePercent != 0) {
shares.nodeOperatorShare = (_poolReward * operatorSharePercent) / PERCENT_DENOMINATOR;
}
shares.validatorShare =
validatorFixedReward -
shares.nodeOperatorShare +
(shares.delegatorsShare * validatorStake) /
totalStake;
}
/// @dev For deployment, the length of stakingTransitionTimeframeLength was chosen not long enough, leading to bonus score losses.
/// see https://github.com/DMDcoin/Beta1/issues/6 for more infos.
function updateStakingTransitionTimeframeLength() external {
stakingTransitionTimeframeLength = 900;
}
}
// slither-disable-end unused-return

@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}

@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}

@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}

@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}

@openzeppelin/contracts/utils/structs/EnumerableSet.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @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 is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 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 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[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._positions[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;
}
}

contracts/interfaces/IBlockRewardHbbft.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
interface IBlockRewardHbbft {
function addToReinsertPot() external payable;
function notifyEarlyEpochEnd() external;
function getGovernanceAddress() external view returns (address);
}

contracts/interfaces/IBonusScoreSystem.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
enum ScoringFactor {
StandByBonus,
NoStandByPenalty,
NoKeyWritePenalty,
BadPerformancePenalty
}
interface IBonusScoreSystem {
function getValidatorScore(address mining) external view returns (uint256);
function rewardStandBy(address mining, uint256 time) external;
function penaliseNoStandBy(address mining, uint256 time) external;
function penaliseNoKeyWrite(address mining) external;
function penaliseBadPerformance(address mining, uint256 time) external;
}

contracts/interfaces/IStakingHbbft.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
interface IStakingHbbft {
struct PoolRewardShares {
uint256 validatorShare;
uint256 nodeOperatorShare;
uint256 delegatorsShare;
}
struct StakingParams {
address _validatorSetContract;
address _bonusScoreContract;
address[] _initialStakingAddresses;
uint256 _delegatorMinStake;
uint256 _candidateMinStake;
uint256 _maxStake;
uint256 _stakingFixedEpochDuration;
uint256 _stakingTransitionTimeframeLength;
uint256 _stakingWithdrawDisallowPeriod;
}
function incrementStakingEpoch() external;
function removePool(address) external;
function removePools() external;
function setStakingEpochStartTime(uint256) external;
function notifyKeyGenFailed() external;
function notifyAvailability(address _stakingAddress) external;
function notifyNetworkOfftimeDetected(uint256) external;
function updatePoolLikelihood(address mining, uint256 validatorScore) external;
function getPoolPublicKey(address _poolAddress)
external
view
returns (bytes memory);
function getPoolsLikelihood()
external
view
returns (uint256[] memory, uint256);
function getPoolsToBeElected() external view returns (address[] memory);
function getPoolsToBeRemoved() external view returns (address[] memory);
function getPoolsInactive() external view returns (address[] memory);
function isPoolActive(address) external view returns (bool);
function isPoolValid(address) external view returns (bool);
function MAX_CANDIDATES() external pure returns (uint256); // solhint-disable-line func-name-mixedcase
function orderedWithdrawAmount(address, address)
external
view
returns (uint256);
function poolDelegators(address) external view returns (address[] memory);
function setValidatorInternetAddress(
address,
bytes16,
bytes2
) external;
function stakeAmount(address, address) external view returns (uint256);
function stakeAmountTotal(address) external view returns (uint256);
function totalStakedAmount() external view returns (uint256);
function stakingWithdrawDisallowPeriod() external view returns (uint256);
function stakingEpoch() external view returns (uint256);
function stakingFixedEpochDuration() external view returns (uint256);
function startTimeOfNextPhaseTransition() external view returns (uint256);
function stakingFixedEpochEndTime() external view returns (uint256);
function stakingEpochStartTime() external view returns (uint256);
function stakingEpochStartBlock() external view returns (uint256);
function restake(
address _poolStakingAddress,
uint256 validatorReward
) external payable;
function snapshotPoolStakeAmounts(
uint256 _epoch,
address _stakingPool
) external;
function getPoolValidatorStakeAmount(
uint256 _epoch,
address _stakingPool
) external view returns (uint256);
}

contracts/interfaces/IValidatorSetHbbft.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
interface IValidatorSetHbbft {
struct ValidatorSetParams {
address blockRewardContract;
address randomContract;
address stakingContract;
address keyGenHistoryContract;
address bonusScoreContract;
address connectivityTrackerContract;
uint256 validatorInactivityThreshold;
}
// Key Generation states of validator.
enum KeyGenMode {
NotAPendingValidator,
WritePart,
WaitForOtherParts,
WriteAck,
WaitForOtherAcks,
AllKeysDone
}
function announceAvailability(uint256, bytes32) external;
function finalizeChange() external;
function newValidatorSet() external;
function setStakingAddress(address, address) external;
function handleFailedKeyGeneration() external;
function isFullHealth() external view returns (bool);
function blockRewardContract() external view returns (address);
function canCallAnnounceAvailability(address _miningAddress)
external
view
returns (bool);
function getPendingValidators() external view returns (address[] memory);
function getPreviousValidators() external view returns (address[] memory);
function getValidators() external view returns (address[] memory);
function isValidator(address) external view returns (bool);
function isValidatorOrPending(address) external view returns (bool);
function isPendingValidator(address) external view returns (bool);
function getPendingValidatorKeyGenerationMode(address)
external
view
returns (KeyGenMode);
function maxValidators() external view returns (uint256);
function miningByStakingAddress(address) external view returns (address);
function randomContract() external view returns (address);
function notifyUnavailability(address) external;
function stakingByMiningAddress(address) external view returns (address);
function publicKeyByStakingAddress(address)
external
view
returns (bytes memory);
function getPublicKey(address) external view returns (bytes memory);
function getStakingContract() external view returns (address);
function validatorAvailableSince(address) external view returns (uint256);
function isValidatorAbandoned(address) external view returns (bool);
function getValidatorCountSweetSpot(uint256)
external
view
returns (uint256);
function getCurrentValidatorsCount() external view returns (uint256);
}

contracts/lib/Errors.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
error Unauthorized();
error ValidatorsListEmpty();
error ZeroAddress();
error ZeroGasPrice();

contracts/lib/ValueGuards.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
abstract contract ValueGuards is OwnableUpgradeable {
// =============================================== Storage ========================================================
/**
* @dev Represents a parameter range for a specific getter function.
* @param getter The getter function signature.
* @param range The range of values for the parameter.
*/
struct ParameterRange {
bytes4 getter;
uint256[] range;
}
struct ValueGuardsStorage {
/**
* @dev A mapping that stores the allowed parameter ranges for each function signature.
*/
mapping(bytes4 => ParameterRange) allowedParameterRange;
}
bytes32 private constant VALUEGUARDS_STORAGE_NAMESPACE = keccak256(abi.encode(uint256(keccak256("valueguards.storage")) - 1)) & ~bytes32(uint256(0xff));
function _getValueGuardsStorage() private pure returns (ValueGuardsStorage storage $) {
bytes32 slot = VALUEGUARDS_STORAGE_NAMESPACE;
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := slot
}
}
// ============================================== Events ==========================================================
/**
* @dev Event emitted when changeable parameters are set.
* @param setter Setter function signature.
* @param getter Getter function signature.
* @param params An array of uint256 values representing the parameters.
*/
event SetChangeableParameter(bytes4 setter, bytes4 getter, uint256[] params);
/**
* @dev Emitted when changeable parameters are removed.
* @param funcSelector The function selector of the removed changeable parameters.
*/
event RemoveChangeableParameter(bytes4 funcSelector);
// ============================================== Errors ==========================================================
error NewValueOutOfRange(uint256 _newVal);
error GetterCallFailed();
// ============================================== Modifiers =======================================================
/**
* @dev Modifier to check if a new value is within the allowed range.
* @param newVal The new value to be checked.
* @notice This modifier is used to ensure that the new value is within the allowed range.
* If the new value is not within the allowed range, the function using this modifier
* will revert with an error message.
*/
modifier withinAllowedRange(uint256 newVal) {
if (!isWithinAllowedRange(msg.sig, newVal)) {
revert NewValueOutOfRange(newVal);
}
_;
}
// =============================================== Initializers ====================================================
/**
* @dev Inits the allowed changeable parameter for a specific setter function.
* @param setter Setter function selector.
* @param getter Getter function selector.
* @param params The array of allowed parameter values.
*/
function __initAllowedChangeableParameter(
bytes4 setter,
bytes4 getter,
uint256[] memory params
) internal onlyInitializing {
ValueGuardsStorage storage $ = _getValueGuardsStorage();
$.allowedParameterRange[setter] = ParameterRange({ getter: getter, range: params });
emit SetChangeableParameter(setter, getter, params);
}
// =============================================== Setters ========================================================
/**
* @dev Sets the allowed changeable parameter for a specific setter function.
* @param setter Setter function selector.
* @param getter Getter function selector.
* @param params The array of allowed parameter values.
*/
function setAllowedChangeableParameter(bytes4 setter, bytes4 getter, uint256[] calldata params) public onlyOwner {
ValueGuardsStorage storage $ = _getValueGuardsStorage();
$.allowedParameterRange[setter] = ParameterRange({ getter: getter, range: params });
emit SetChangeableParameter(setter, getter, params);
}
/**
* @dev Removes the allowed changeable parameter for a given function selector.
* @param funcSelector The function selector for which the allowed changeable parameter should be removed.
*/
function removeAllowedChangeableParameter(bytes4 funcSelector) public onlyOwner {
ValueGuardsStorage storage $ = _getValueGuardsStorage();
delete $.allowedParameterRange[funcSelector];
emit RemoveChangeableParameter(funcSelector);
}
// =============================================== Getters ========================================================
/**
* @dev Checks if the given `newVal` is within the allowed range for the specified function selector.
* @param funcSelector The function selector.
* @param newVal The new value to be checked.
* @return A boolean indicating whether the `newVal` is within the allowed range.
*/
function isWithinAllowedRange(bytes4 funcSelector, uint256 newVal) public view returns (bool) {
ValueGuardsStorage storage $ = _getValueGuardsStorage();
ParameterRange memory allowedRange = $.allowedParameterRange[funcSelector];
if (allowedRange.range.length == 0) {
return false;
}
uint256[] memory range = allowedRange.range;
uint256 currVal = _getValueWithSelector(allowedRange.getter);
for (uint256 i = 0; i < range.length; i++) {
if (range[i] == currVal) {
uint256 leftVal = (i > 0) ? range[i - 1] : range[0];
uint256 rightVal = (i < range.length - 1) ? range[i + 1] : range[range.length - 1];
return !(newVal != leftVal && newVal != rightVal);
}
}
return false;
}
function getAllowedParamsRange(string memory _selector) external view returns (ParameterRange memory) {
return _getValueGuardsStorage().allowedParameterRange[bytes4(keccak256(bytes(_selector)))];
}
function getAllowedParamsRangeWithSelector(bytes4 _selector) external view returns (ParameterRange memory) {
return _getValueGuardsStorage().allowedParameterRange[_selector];
}
// =============================================== Internal ========================================================
/**
* @dev Internal function to get the value of a contract state variable using a getter function.
* @param getterSelector The selector of the getter function.
* @return The value of the contract state variable.
*/
function _getValueWithSelector(bytes4 getterSelector) private view returns (uint256) {
bytes memory payload = abi.encodeWithSelector(getterSelector);
(bool success, bytes memory result) = address(this).staticcall(payload);
if (!success) {
revert GetterCallFailed();
}
return abi.decode(result, (uint256));
}
}

contracts/utils/TransferUtils.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
library TransferUtils {
error InsufficientBalance();
error TransferFailed(address recipient, uint256 amount);
function transferNative(address recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert InsufficientBalance();
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = recipient.call{ value: amount }("");
if (!success) {
revert TransferFailed(recipient, amount);
}
}
}

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"error","name":"CannotClaimWithdrawOrderYet","inputs":[{"type":"address","name":"pool","internalType":"address"},{"type":"address","name":"staker","internalType":"address"}]},{"type":"error","name":"GetterCallFailed","inputs":[]},{"type":"error","name":"InitialStakingPoolsListEmpty","inputs":[]},{"type":"error","name":"InsufficientBalance","inputs":[]},{"type":"error","name":"InsufficientStakeAmount","inputs":[{"type":"address","name":"pool","internalType":"address"},{"type":"address","name":"delegator","internalType":"address"}]},{"type":"error","name":"InvalidFixedEpochDuration","inputs":[]},{"type":"error","name":"InvalidInitialStakeAmount","inputs":[{"type":"uint256","name":"candidateStake","internalType":"uint256"},{"type":"uint256","name":"delegatorStake","internalType":"uint256"}]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"InvalidIpAddressesCount","inputs":[]},{"type":"error","name":"InvalidMaxStakeAmount","inputs":[]},{"type":"error","name":"InvalidMoveStakePoolsAddress","inputs":[]},{"type":"error","name":"InvalidNodeOperatorConfiguration","inputs":[{"type":"address","name":"_operator","internalType":"address"},{"type":"uint256","name":"_share","internalType":"uint256"}]},{"type":"error","name":"InvalidNodeOperatorShare","inputs":[{"type":"uint256","name":"_share","internalType":"uint256"}]},{"type":"error","name":"InvalidOrderWithdrawAmount","inputs":[{"type":"address","name":"pool","internalType":"address"},{"type":"address","name":"delegator","internalType":"address"},{"type":"int256","name":"amount","internalType":"int256"}]},{"type":"error","name":"InvalidPublicKeysCount","inputs":[]},{"type":"error","name":"InvalidStakingFixedEpochDuration","inputs":[]},{"type":"error","name":"InvalidStakingTransitionTimeframe","inputs":[]},{"type":"error","name":"InvalidTransitionTimeFrame","inputs":[]},{"type":"error","name":"InvalidWithdrawAmount","inputs":[{"type":"address","name":"pool","internalType":"address"},{"type":"address","name":"delegator","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"error","name":"MaxAllowedWithdrawExceeded","inputs":[{"type":"uint256","name":"allowed","internalType":"uint256"},{"type":"uint256","name":"desired","internalType":"uint256"}]},{"type":"error","name":"MaxPoolsCountExceeded","inputs":[]},{"type":"error","name":"NewValueOutOfRange","inputs":[{"type":"uint256","name":"_newVal","internalType":"uint256"}]},{"type":"error","name":"NoStakesToRecover","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"NotPayable","inputs":[]},{"type":"error","name":"OnlyOncePerEpoch","inputs":[{"type":"uint256","name":"_epoch","internalType":"uint256"}]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"error","name":"PoolAbandoned","inputs":[{"type":"address","name":"pool","internalType":"address"}]},{"type":"error","name":"PoolCannotBeRemoved","inputs":[{"type":"address","name":"pool","internalType":"address"}]},{"type":"error","name":"PoolEmpty","inputs":[{"type":"address","name":"pool","internalType":"address"}]},{"type":"error","name":"PoolNotExist","inputs":[{"type":"address","name":"pool","internalType":"address"}]},{"type":"error","name":"PoolStakeLimitExceeded","inputs":[{"type":"address","name":"pool","internalType":"address"},{"type":"address","name":"delegator","internalType":"address"}]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"error","name":"TransferFailed","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"error","name":"Unauthorized","inputs":[]},{"type":"error","name":"WithdrawNotAllowed","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"error","name":"ZeroGasPrice","inputs":[]},{"type":"error","name":"ZeroWidthrawAmount","inputs":[]},{"type":"error","name":"ZeroWidthrawDisallowPeriod","inputs":[]},{"type":"event","name":"ClaimedOrderedWithdrawal","inputs":[{"type":"address","name":"fromPoolStakingAddress","internalType":"address","indexed":true},{"type":"address","name":"staker","internalType":"address","indexed":true},{"type":"uint256","name":"stakingEpoch","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"GatherAbandonedStakes","inputs":[{"type":"address","name":"caller","internalType":"address","indexed":true},{"type":"address","name":"stakingAddress","internalType":"address","indexed":true},{"type":"uint256","name":"gatheredFunds","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"type":"uint64","name":"version","internalType":"uint64","indexed":false}],"anonymous":false},{"type":"event","name":"MovedStake","inputs":[{"type":"address","name":"fromPoolStakingAddress","internalType":"address","indexed":false},{"type":"address","name":"toPoolStakingAddress","internalType":"address","indexed":true},{"type":"address","name":"staker","internalType":"address","indexed":true},{"type":"uint256","name":"stakingEpoch","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OrderedWithdrawal","inputs":[{"type":"address","name":"fromPoolStakingAddress","internalType":"address","indexed":true},{"type":"address","name":"staker","internalType":"address","indexed":true},{"type":"uint256","name":"stakingEpoch","internalType":"uint256","indexed":true},{"type":"int256","name":"amount","internalType":"int256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"PlacedStake","inputs":[{"type":"address","name":"toPoolStakingAddress","internalType":"address","indexed":true},{"type":"address","name":"staker","internalType":"address","indexed":true},{"type":"uint256","name":"stakingEpoch","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RecoverAbandonedStakes","inputs":[{"type":"address","name":"caller","internalType":"address","indexed":true},{"type":"uint256","name":"reinsertShare","internalType":"uint256","indexed":false},{"type":"uint256","name":"governanceShare","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RemoveChangeableParameter","inputs":[{"type":"bytes4","name":"funcSelector","internalType":"bytes4","indexed":false}],"anonymous":false},{"type":"event","name":"RestakeReward","inputs":[{"type":"address","name":"poolStakingAddress","internalType":"address","indexed":true},{"type":"uint256","name":"stakingEpoch","internalType":"uint256","indexed":true},{"type":"uint256","name":"validatorReward","internalType":"uint256","indexed":false},{"type":"uint256","name":"delegatorsReward","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetChangeableParameter","inputs":[{"type":"bytes4","name":"setter","internalType":"bytes4","indexed":false},{"type":"bytes4","name":"getter","internalType":"bytes4","indexed":false},{"type":"uint256[]","name":"params","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"SetDelegatorMinStake","inputs":[{"type":"uint256","name":"minStake","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetNodeOperator","inputs":[{"type":"address","name":"poolStakingAddress","internalType":"address","indexed":true},{"type":"address","name":"nodeOperatorAddress","internalType":"address","indexed":true},{"type":"uint256","name":"operatorShare","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrewStake","inputs":[{"type":"address","name":"fromPoolStakingAddress","internalType":"address","indexed":true},{"type":"address","name":"staker","internalType":"address","indexed":true},{"type":"uint256","name":"stakingEpoch","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_CANDIDATES","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_NODE_OPERATOR_SHARE_PERCENT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PERCENT_DENOMINATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"abandonedAndRemoved","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"addPool","inputs":[{"type":"address","name":"_miningAddress","internalType":"address"},{"type":"address","name":"_nodeOperatorAddress","internalType":"address"},{"type":"uint256","name":"_operatorShare","internalType":"uint256"},{"type":"bytes","name":"_publicKey","internalType":"bytes"},{"type":"bytes16","name":"_ip","internalType":"bytes16"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"areStakeAndWithdrawAllowed","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IBonusScoreSystem"}],"name":"bonusScoreContract","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"candidateMinStake","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimOrderedWithdraw","inputs":[{"type":"address","name":"_poolStakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentKeyGenExtraTimeWindow","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"delegatorMinStake","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct ValueGuards.ParameterRange","components":[{"type":"bytes4","name":"getter","internalType":"bytes4"},{"type":"uint256[]","name":"range","internalType":"uint256[]"}]}],"name":"getAllowedParamsRange","inputs":[{"type":"string","name":"_selector","internalType":"string"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct ValueGuards.ParameterRange","components":[{"type":"bytes4","name":"getter","internalType":"bytes4"},{"type":"uint256[]","name":"range","internalType":"uint256[]"}]}],"name":"getAllowedParamsRangeWithSelector","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes16","name":"","internalType":"bytes16"},{"type":"bytes2","name":"","internalType":"bytes2"}],"name":"getPoolInternetAddress","inputs":[{"type":"address","name":"_poolAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"getPoolPublicKey","inputs":[{"type":"address","name":"_poolAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getPoolValidatorStakeAmount","inputs":[{"type":"uint256","name":"_epoch","internalType":"uint256"},{"type":"address","name":"_stakingPool","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getPools","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getPoolsInactive","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"likelihoods","internalType":"uint256[]"},{"type":"uint256","name":"sum","internalType":"uint256"}],"name":"getPoolsLikelihood","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getPoolsToBeElected","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getPoolsToBeRemoved","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"incrementStakingEpoch","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_contractOwner","internalType":"address"},{"type":"tuple","name":"stakingParams","internalType":"struct IStakingHbbft.StakingParams","components":[{"type":"address","name":"_validatorSetContract","internalType":"address"},{"type":"address","name":"_bonusScoreContract","internalType":"address"},{"type":"address[]","name":"_initialStakingAddresses","internalType":"address[]"},{"type":"uint256","name":"_delegatorMinStake","internalType":"uint256"},{"type":"uint256","name":"_candidateMinStake","internalType":"uint256"},{"type":"uint256","name":"_maxStake","internalType":"uint256"},{"type":"uint256","name":"_stakingFixedEpochDuration","internalType":"uint256"},{"type":"uint256","name":"_stakingTransitionTimeframeLength","internalType":"uint256"},{"type":"uint256","name":"_stakingWithdrawDisallowPeriod","internalType":"uint256"}]},{"type":"bytes32[]","name":"_publicKeys","internalType":"bytes32[]"},{"type":"bytes16[]","name":"_internetAddresses","internalType":"bytes16[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isPoolActive","inputs":[{"type":"address","name":"_stakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isPoolValid","inputs":[{"type":"address","name":"_stakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isWithinAllowedRange","inputs":[{"type":"bytes4","name":"funcSelector","internalType":"bytes4"},{"type":"uint256","name":"newVal","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxStakeAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxWithdrawAllowed","inputs":[{"type":"address","name":"_poolStakingAddress","internalType":"address"},{"type":"address","name":"_staker","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxWithdrawOrderAllowed","inputs":[{"type":"address","name":"_poolStakingAddress","internalType":"address"},{"type":"address","name":"_staker","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"moveStake","inputs":[{"type":"address","name":"_fromPoolStakingAddress","internalType":"address"},{"type":"address","name":"_toPoolStakingAddress","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"notifyAvailability","inputs":[{"type":"address","name":"_stakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"notifyKeyGenFailed","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"notifyNetworkOfftimeDetected","inputs":[{"type":"uint256","name":"detectedOfflineTime","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"orderWithdraw","inputs":[{"type":"address","name":"_poolStakingAddress","internalType":"address"},{"type":"int256","name":"_amount","internalType":"int256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"orderWithdrawEpoch","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"orderedWithdrawAmount","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"orderedWithdrawAmountTotal","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"poolDelegators","inputs":[{"type":"address","name":"_poolStakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"poolDelegatorsInactive","inputs":[{"type":"address","name":"_poolStakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"publicKey","internalType":"bytes"},{"type":"bytes16","name":"internetAddress","internalType":"bytes16"},{"type":"bytes2","name":"port","internalType":"bytes2"}],"name":"poolInfo","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"poolNodeOperator","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"poolNodeOperatorLastChangeEpoch","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"poolNodeOperatorShare","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"poolToBeElectedIndex","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverAbandonedStakes","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeAllowedChangeableParameter","inputs":[{"type":"bytes4","name":"funcSelector","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeMyPool","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removePool","inputs":[{"type":"address","name":"_stakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removePools","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"restake","inputs":[{"type":"address","name":"_poolStakingAddress","internalType":"address"},{"type":"uint256","name":"_validatorMinRewardPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAllowedChangeableParameter","inputs":[{"type":"bytes4","name":"setter","internalType":"bytes4"},{"type":"bytes4","name":"getter","internalType":"bytes4"},{"type":"uint256[]","name":"params","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDelegatorMinStake","inputs":[{"type":"uint256","name":"_minStake","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setNodeOperator","inputs":[{"type":"address","name":"_operatorAddress","internalType":"address"},{"type":"uint256","name":"_operatorShare","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPoolInfo","inputs":[{"type":"bytes","name":"_publicKey","internalType":"bytes"},{"type":"bytes16","name":"_ip","internalType":"bytes16"},{"type":"bytes2","name":"_port","internalType":"bytes2"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setStakingEpochStartTime","inputs":[{"type":"uint256","name":"_timestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setValidatorInternetAddress","inputs":[{"type":"address","name":"_validatorAddress","internalType":"address"},{"type":"bytes16","name":"_ip","internalType":"bytes16"},{"type":"bytes2","name":"_port","internalType":"bytes2"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"snapshotPoolStakeAmounts","inputs":[{"type":"uint256","name":"_epoch","internalType":"uint256"},{"type":"address","name":"_stakingPool","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"snapshotPoolTotalStakeAmount","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"snapshotPoolValidatorStakeAmount","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"stake","inputs":[{"type":"address","name":"_toPoolStakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakeAmount","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakeAmountByCurrentEpoch","inputs":[{"type":"address","name":"_poolStakingAddress","internalType":"address"},{"type":"address","name":"_staker","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakeAmountTotal","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakingEpoch","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakingEpochStartBlock","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakingEpochStartTime","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakingFixedEpochDuration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakingFixedEpochEndTime","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakingTransitionTimeframeLength","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakingWithdrawDisallowPeriod","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"startTimeOfNextPhaseTransition","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalStakedAmount","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updatePoolLikelihood","inputs":[{"type":"address","name":"mining","internalType":"address"},{"type":"uint256","name":"validatorScore","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateStakingTransitionTimeframeLength","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IValidatorSetHbbft"}],"name":"validatorSetContract","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[{"type":"address","name":"_fromPoolStakingAddress","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
            

Contract Creation Code

0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615e30806100d96000396000f3fe60806040526004361061055c5760003560e01c8063862be287116102ca578063c6912cc011610179578063e9ab0300116100d6578063f3fef3a31161008a578063f6c578611161006f578063f6c5786114610f96578063f9d4d54414610fb6578063fa4aaf4714610fe357600080fd5b8063f3fef3a314610f62578063f494250114610f8257600080fd5b8063ee435f55116100bb578063ee435f5514610f17578063f078609614610f2c578063f2fde38b14610f4257600080fd5b8063e9ab030014610eca578063edd7db7514610f0257600080fd5b8063d5c4b08a1161012d578063dadee88a11610112578063dadee88a14610e68578063df6f55f514610e95578063dfc8bf4e14610eaa57600080fd5b8063d5c4b08a14610e3a578063da7a9b6a14610e5257600080fd5b8063cfef14411161015e578063cfef144114610de5578063d0ac76f614610e05578063d290c21d14610e2557600080fd5b8063c6912cc014610db0578063c6af311a14610dc557600080fd5b8063a420596711610227578063ae1aaf80116101db578063b710c15d116101c0578063b710c15d14610d43578063ba08d23714610d70578063bf348f8614610d9057600080fd5b8063ae1aaf8014610d0d578063b61ed63a14610d2357600080fd5b8063a697ecff1161020c578063a697ecff14610ca0578063a711e6a114610cd8578063adddc0cf14610cf857600080fd5b8063a420596714610c53578063a5d54f6514610c8b57600080fd5b80639a7b5f111161027e5780639e6c2959116102635780639e6c295914610bfd5780639e72c63514610c135780639ea8082b14610c3357600080fd5b80639a7b5f1114610bb85780639b03d74414610be757600080fd5b8063921e274b116102af578063921e274b14610b62578063950a651314610b75578063957950a714610b9557600080fd5b8063862be28714610b055780638da5cb5b14610b2557600080fd5b80634f9a8d8f11610426578063715018a6116103835780637b0a0f9b116103375780637d8149db1161031c5780637d8149db14610a985780638247a23914610ab857806384725c7614610ad857600080fd5b80637b0a0f9b14610a585780637b15b94c14610a7857600080fd5b806373c218031161036857806373c21803146109d2578063750dd2a1146109f2578063794c0c6814610a4257600080fd5b8063715018a614610987578063728345db1461099c57600080fd5b80635fef7643116103da578063673a2a1f116103bf578063673a2a1f1461092f5780636bda1577146109515780637069e7461461097157600080fd5b80635fef7643146108e3578063615f2b71146108f957600080fd5b8063567e98f91161040b578063567e98f9146108975780635b4eafe0146108ad5780635d80ca32146108cd57600080fd5b80634f9a8d8f146108545780635267e1d61461086a57600080fd5b806326476204116104d45780633b7d0946116104885780634160d3861161046d5780634160d386146107e75780634346845f146108075780634e9b426d1461082757600080fd5b80633b7d0946146107b45780633f3afe01146107d457600080fd5b80633219d600116104b95780633219d600146107495780633715426314610789578063379b046a1461079e57600080fd5b806326476204146107165780632bafde8d1461072957600080fd5b80631555371c1161052b57806320c07cd81161051057806320c07cd8146106cb57806322e3d986146106e1578063251441a9146106f657600080fd5b80631555371c1461063a5780631fb31e431461064f57600080fd5b8063028b8bdb1461057f5780630ac6e291146105a85780630b770cdf146105ca5780631345b8a51461060257600080fd5b3661057a57604051631574f9f360e01b815260040160405180910390fd5b600080fd5b34801561058b57600080fd5b5061059560145481565b6040519081526020015b60405180910390f35b3480156105b457600080fd5b506105c86105c336600461534f565b611010565b005b3480156105d657600080fd5b506024546105ea906001600160a01b031681565b6040516001600160a01b03909116815260200161059f565b34801561060e57600080fd5b5061059561061d3660046153ff565b602160209081526000928352604080842090915290825290205481565b34801561064657600080fd5b506105c86115a7565b34801561065b57600080fd5b5061069961066a36600461542f565b6001600160a01b03166000908152601e6020526040902060010154608081901b91600160801b90910460f01b90565b604080516fffffffffffffffffffffffffffffffff1990931683526001600160f01b031990911660208301520161059f565b3480156106d757600080fd5b506105956107d081565b3480156106ed57600080fd5b50610595611619565b34801561070257600080fd5b506105c861071136600461544c565b611660565b6105c861072436600461542f565b61171e565b34801561073557600080fd5b506105c861074436600461548d565b6117a1565b34801561075557600080fd5b5061077961076436600461542f565b601f6020526000908152604090205460ff1681565b604051901515815260200161059f565b34801561079557600080fd5b506105c8611822565b3480156107aa57600080fd5b50610595601a5481565b3480156107c057600080fd5b506105c86107cf36600461542f565b611c1d565b6105c86107e23660046154a6565b611c53565b3480156107f357600080fd5b506105c86108023660046154a6565b611f89565b34801561081357600080fd5b506105c86108223660046154ef565b612028565b34801561083357600080fd5b5061084761084236600461542f565b61212e565b60405161059f91906155a0565b34801561086057600080fd5b5061059560165481565b34801561087657600080fd5b5061059561088536600461542f565b601b6020526000908152604090205481565b3480156108a357600080fd5b50610595601c5481565b3480156108b957600080fd5b506105956108c83660046153ff565b6121da565b3480156108d957600080fd5b50610595600e5481565b3480156108ef57600080fd5b50610595600c5481565b34801561090557600080fd5b506105ea61091436600461542f565b6025602052600090815260409020546001600160a01b031681565b34801561093b57600080fd5b50610944612204565b60405161059f91906155b3565b34801561095d57600080fd5b5061059561096c366004615600565b612215565b34801561097d57600080fd5b5061059560195481565b34801561099357600080fd5b506105c8612395565b3480156109a857600080fd5b506105956109b73660046153ff565b60208080526000928352604080842090915290825290205481565b3480156109de57600080fd5b506109446109ed36600461542f565b6123a9565b3480156109fe57600080fd5b50610595610a0d366004615600565b6001600160a01b039182166000908152600b602090815260408083209390941682529182528281206015548252909152205490565b348015610a4e57600080fd5b5061059560155481565b348015610a6457600080fd5b506105c8610a73366004615667565b6123cd565b348015610a8457600080fd5b506105c8610a9336600461542f565b61244a565b348015610aa457600080fd5b506105c8610ab33660046156ac565b6124b1565b348015610ac457600080fd5b506105c8610ad336600461548d565b612533565b348015610ae457600080fd5b50610595610af336600461542f565b60266020526000908152604090205481565b348015610b1157600080fd5b506105c8610b20366004615709565b612566565b348015610b3157600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166105ea565b6105c8610b7036600461576e565b6125c9565b348015610b8157600080fd5b50610595610b90366004615600565b612712565b348015610ba157600080fd5b50610baa61285a565b60405161059f92919061582c565b348015610bc457600080fd5b50610bd8610bd336600461542f565b6128bd565b60405161059f9392919061584e565b348015610bf357600080fd5b5061059560185481565b348015610c0957600080fd5b5061059561271081565b348015610c1f57600080fd5b506105c8610c2e36600461548d565b612971565b348015610c3f57600080fd5b50610944610c4e36600461542f565b6129bc565b348015610c5f57600080fd5b50610595610c6e366004615600565b601160209081526000928352604080842090915290825290205481565b348015610c9757600080fd5b506109446129e0565b348015610cac57600080fd5b50610595610cbb366004615600565b601360209081526000928352604080842090915290825290205481565b348015610ce457600080fd5b50610779610cf336600461542f565b612a42565b348015610d0457600080fd5b506105c8612a4e565b348015610d1957600080fd5b5061059560175481565b348015610d2f57600080fd5b506105c8610d3e3660046154a6565b612a93565b348015610d4f57600080fd5b50610d63610d5e3660046158ab565b612dfa565b60405161059f919061595c565b348015610d7c57600080fd5b50610779610d8b36600461598a565b612eaa565b348015610d9c57600080fd5b50610d63610dab3660046156ac565b613085565b348015610dbc57600080fd5b5061059561312a565b348015610dd157600080fd5b506105c8610de03660046154a6565b613149565b348015610df157600080fd5b506105c8610e0036600461542f565b6131f3565b348015610e1157600080fd5b506105c8610e203660046153ff565b6133c6565b348015610e3157600080fd5b50610944613505565b348015610e4657600080fd5b506105c8610384601755565b348015610e5e57600080fd5b50610595600d5481565b348015610e7457600080fd5b50610595610e8336600461542f565b60106020526000908152604090205481565b348015610ea157600080fd5b50610944613511565b348015610eb657600080fd5b50601d546105ea906001600160a01b031681565b348015610ed657600080fd5b50610595610ee5366004615600565b600f60209081526000928352604080842090915290825290205481565b348015610f0e57600080fd5b506105c861351d565b348015610f2357600080fd5b506105c861365d565b348015610f3857600080fd5b50610595610bb881565b348015610f4e57600080fd5b506105c8610f5d36600461542f565b6136a3565b348015610f6e57600080fd5b506105c8610f7d3660046154a6565b6136de565b348015610f8e57600080fd5b506001610779565b348015610fa257600080fd5b50610779610fb136600461542f565b613797565b348015610fc257600080fd5b50610595610fd136600461542f565b60276020526000908152604090205481565b348015610fef57600080fd5b50610595610ffe36600461542f565b60126020526000908152604090205481565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561105b5750825b905060008267ffffffffffffffff1660011480156110785750303b155b905081158015611086575080155b156110a45760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156110d857845468ff00000000000000001916680100000000000000001785555b6001600160a01b038b166110ff5760405163d92e233d60e01b815260040160405180910390fd5b6111088a6137b4565b8761111660408c018c6159a6565b61112291506002615a06565b1461113f5760405162d6988760e81b815260040160405180910390fd5b8561114d60408c018c6159a6565b90501461116d576040516320041afd60e01b815260040160405180910390fd5b6111768b613917565b61117e613928565b61118b60208b018b61542f565b601d80546001600160a01b0319166001600160a01b03929092169190911790556111bb60408b0160208c0161542f565b602480546001600160a01b0319166001600160a01b03929092169190911790553660006111eb60408d018d6159a6565b9150915060005b8181101561140e57600083838381811061120e5761120e615a1d565b9050602002016020810190611223919061542f565b6001600160a01b03160361124a5760405163d92e233d60e01b815260040160405180910390fd5b61127b83838381811061125f5761125f615a1d565b9050602002016020810190611274919061542f565b6000613938565b6112aa83838381811061129057611290615a1d565b90506020020160208101906112a5919061542f565b613998565b8b8b6112b7836002615a06565b8181106112c6576112c6615a1d565b905060200201358c8c8360026112dc9190615a06565b6112e7906001615a33565b8181106112f6576112f6615a1d565b90506020020135604051602001611317929190918252602082015260400190565b604051602081830303815290604052601e600085858581811061133c5761133c615a1d565b9050602002016020810190611351919061542f565b6001600160a01b031681526020810191909152604001600020906113759082615ad0565b5089898281811061138857611388615a1d565b905060200201602081019061139d9190615b90565b601e60008585858181106113b3576113b3615a1d565b90506020020160208101906113c8919061542f565b6001600160a01b031681526020810191909152604001600020600190810180546fffffffffffffffffffffffffffffffff191660809390931c92909217909155016111f2565b5060408051600580825260c082019092526000916020820160a0803683370190505090506802b5e3af16b18800008160008151811061144f5761144f615a1d565b60200260200101818152505068056bc75e2d631000008160018151811061147857611478615a1d565b602002602001018181525050680821ab0d4414980000816002815181106114a1576114a1615a1d565b602002602001018181525050680ad78ebc5ac6200000816003815181106114ca576114ca615a1d565b602002602001018181525050680d8d726b7177a80000816004815181106114f3576114f3615a1d565b6020908102919091010152611517632bafde8d60e01b636d3d4db560e11b836139ad565b50505060608a0135600d5560808a0135600c5560a08a0135600e5560c08a01356016556101008a01356014554260185560e08a0135601755831561159a57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050505050565b601d546001600160a01b031633146115d1576040516282b42960e81b815260040160405180910390fd5b60006115dd6004613a62565b905060005b81518110156116155761160d82828151811061160057611600615a1d565b6020026020010151613a76565b6001016115e2565b5050565b601854601654600091901561162f576001611632565b60005b60ff16601a54601654836116469190615a33565b6116509190615a33565b61165a9190615bab565b91505090565b3a60000361168157604051630e661aed60e41b815260040160405180910390fd5b816001600160a01b0316836001600160a01b0316036116b357604051630a5eddd560e01b815260040160405180910390fd5b336116bf848284613abc565b6116ca838284613d6a565b601554604080516001600160a01b0387811682526020820186905280851692908716917f4480d8e4b1e9095b94bf513961d26fe1d32386ebdd103d18fe8738cf4b2223ff910160405180910390a450505050565b3a60000361173f57604051630e661aed60e41b815260040160405180910390fd5b333461174c838383613d6a565b601554826001600160a01b0316846001600160a01b03167f2273de02cb1f69ba6259d22c4bc22c60e4c94c193265ef6afee324a04a9b6d228460405161179491815260200190565b60405180910390a4505050565b6117a96140bb565b806117c06000356001600160e01b03191682612eaa565b6117e5576040516373330d9b60e01b8152600481018290526024015b60405180910390fd5b600d8290556040518281527ffee02ce7aa40f9c49eaabd26d404fa88714b97cb209af8954cfd5eeb8213b93e906020015b60405180910390a15050565b3a60000361184357604051630e661aed60e41b815260040160405180910390fd5b6000806118506002613a62565b9050805160000361187457604051631c369b4560e21b815260040160405180910390fd5b60005b8151811015611a5c57600082828151811061189457611894615a1d565b602002602001015190506118a781614116565b8061191d5750601d54604051639d6fc1d160e01b81526001600160a01b03838116600483015290911690639d6fc1d190602401602060405180830381865afa1580156118f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191b9190615bbe565b155b156119285750611a54565b611933600282614154565b506001600160a01b0381166000908152601f60209081526040808320805460ff19166001179055601b9091528120805490829055601c80549192839261197a908490615bab565b909155506000905061198b836129bc565b905060005b81518110156119f65760008282815181106119ad576119ad615a1d565b6020908102919091018101516001600160a01b03808816600090815260138452604080822092841682529190935282209190915590506119ed8582614169565b50600101611990565b50611a018287615a33565b9550826001600160a01b0316336001600160a01b03167f8e6a4ccd7dccdca9ac211d00fcc3fa7c71be75ff73d3a35f63ea023173cc100484604051611a4891815260200190565b60405180910390a35050505b600101611877565b5081600003611a7e57604051631c369b4560e21b815260040160405180910390fd5b6000611a8b600284615be0565b90506000611a998285615bab565b90506000601d60009054906101000a90046001600160a01b03166001600160a01b03166356b54bae6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611af0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b149190615c02565b90506000816001600160a01b031663732524946040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b7a9190615c02565b9050816001600160a01b031663af182535846040518263ffffffff1660e01b81526004016000604051808303818588803b158015611bb757600080fd5b505af1158015611bcb573d6000803e3d6000fd5b5050505050611bda81856141cb565b604080518481526020810186905233917f1c113e6bae9530fea40323e612aeb0cb7817dedb5a0b0f6bdfbff97d55920dc7910160405180910390a2505050505050565b601d546001600160a01b03163314611c47576040516282b42960e81b815260040160405180910390fd5b611c5081613a76565b50565b601d60009054906101000a90046001600160a01b03166001600160a01b03166356b54bae6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ca6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cca9190615c02565b6001600160a01b0316336001600160a01b031614611cfa576040516282b42960e81b815260040160405180910390fd5b3415611615576015546000908152602080805260408083206001600160a01b03861684529091528120543491611d31858486614272565b90506000611d3e866129bc565b905060005b8151811015611e8d57600084611d756015548a868681518110611d6857611d68615a1d565b6020026020010151614391565b8560400151611d849190615a06565b611d8e9190615be0565b905080601360008a6001600160a01b03166001600160a01b031681526020019081526020016000206000858581518110611dca57611dca615a1d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000828254611e019190615a33565b90915550506001600160a01b0388166000908152600b602052604081208451839290869086908110611e3557611e35615a1d565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600060155481526020019081526020016000206000828254611e7f9190615a33565b909155505050600101611d43565b50602082015115611ea657611ea6868360200151614432565b81516001600160a01b038716600090815260136020908152604080832090915281208054909190611ed8908490615a33565b90915550506001600160a01b0386166000908152601b602052604081208054869290611f05908490615a33565b9250508190555083601c6000828254611f1e9190615a33565b90915550611f2d9050866144f9565b60155482516001600160a01b038816907f72093068b9f28053bd924ac15d7710b987f9b6ef1e0f89f47d8b4bd7cac776dc90611f698189615bab565b6040805192835260208301919091520160405180910390a3505050505050565b601d546040516253517560e01b81523360048201526000916001600160a01b0316906253517590602401602060405180830381865afa158015611fd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ff49190615c02565b6001600160a01b03160361201d57604051632670461960e11b81523360048201526024016117dc565b6116153383836145e5565b6120306140bb565b600061203a61471d565b90506040518060400160405280857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200184848080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509390945250506001600160e01b031988168152602084815260409091208351815463ffffffff191660e09190911c1781558382015180519193506120e6926001850192910190615274565b509050507f3665bf9cd0ba4ddceeec259e21dcf8a4510f3b1130bd42e950828e69d85408ba8585858560405161211f9493929190615c1f565b60405180910390a15050505050565b6001600160a01b0381166000908152601e6020526040902080546060919061215590615a46565b80601f016020809104026020016040519081016040528092919081815260200182805461218190615a46565b80156121ce5780601f106121a3576101008083540402835291602001916121ce565b820191906000526020600020905b8154815290600101906020018083116121b157829003601f168201915b50505050509050919050565b60008281526021602090815260408083206001600160a01b03851684529091529020545b92915050565b60606122106000613a62565b905090565b601d546040516253517560e01b81526001600160a01b03848116600483015260009283929116906253517590602401602060405180830381865afa158015612261573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122859190615c02565b90506001600160a01b0384166000908152601f602052604090205460ff16156122b25760009150506121fe565b6001600160a01b03848116600090815260136020908152604080832087851684529091529081902054601d54915163a0d16cad60e01b815284841660048201529092919091169063a0d16cad90602401602060405180830381865afa15801561231f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123439190615bbe565b6123505791506121fe9050565b6001600160a01b038581166000908152600b6020908152604080832093881683529281528282206015548352905220548082111561238c578091505b50949350505050565b61239d6140bb565b6123a7600061477f565b565b6001600160a01b0381166000908152600a602052604090206060906121fe90613a62565b601d546001600160a01b031633146123f7576040516282b42960e81b815260040160405180910390fd5b6001600160a01b039092166000908152601e60205260409020600101805460f09390931c600160801b0271ffffffffffffffffffffffffffffffffffff1990931660809290921c91909117919091179055565b601d546001600160a01b03163314612474576040516282b42960e81b815260040160405180910390fd5b600c546001600160a01b038216600090815260136020908152604080832090915290205410611c50576124a8816001613938565b611c50816144f9565b6124b96140bb565b60006124c361471d565b6001600160e01b031983166000908152602082905260408120805463ffffffff191681559192506124f760018301826152bf565b50506040516001600160e01b0319831681527fed27cb02231782dadf13473a7828cb980c4d685791b7a3136dde00f8c3594cb690602001611816565b601d546001600160a01b0316331461255d576040516282b42960e81b815260040160405180910390fd5b60185543601955565b336000908152601e60205260409020612580848683615c8c565b50336000908152601e60205260409020600101805460f09290921c600160801b0271ffffffffffffffffffffffffffffffffffff1990921660809390931c929092171790555050565b3a6000036125ea57604051630e661aed60e41b815260040160405180910390fd5b601d54604051630526083960e11b81526001600160a01b0388811660048301523360248301819052923492911690630a4c107290604401600060405180830381600087803b15801561263b57600080fd5b505af115801561264f573d6000803e3d6000fd5b505050506001600160a01b0382166000908152601e60205260409020612676858783615c8c565b506001600160a01b0382166000908152601e6020526040902060010180546fffffffffffffffffffffffffffffffff1916608085901c1790556126ba8288886145e5565b6126c5828383613d6a565b6015546040518281526001600160a01b0384169081907f2273de02cb1f69ba6259d22c4bc22c60e4c94c193265ef6afee324a04a9b6d229060200160405180910390a45050505050505050565b601d546040516253517560e01b81526001600160a01b03848116600483015260009283929116906253517590602401602060405180830381865afa15801561275e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127829190615c02565b9050601d5460405163a0d16cad60e01b81526001600160a01b0383811660048301529091169063a0d16cad90602401602060405180830381865afa1580156127ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f29190615bbe565b6128005760009150506121fe565b6001600160a01b038481166000818152600b6020908152604080832094881680845294825280832060155484528252808320549383526013825280832094835293905291909120546128529190615bab565b949350505050565b606060006007600854818054806020026020016040519081016040528092919081815260200182805480156128ae57602002820191906000526020600020905b81548152602001906001019080831161289a575b50505050509150915091509091565b601e602052600090815260409020805481906128d890615a46565b80601f016020809104026020016040519081016040528092919081815260200182805461290490615a46565b80156129515780601f1061292657610100808354040283529160200191612951565b820191906000526020600020905b81548152906001019060200180831161293457829003601f168201915b50505060019093015491925050608081901b90600160801b900460f01b83565b601d546001600160a01b0316331461299b576040516282b42960e81b815260040160405180910390fd5b60175481601a546129ac9190615a33565b6129b69190615a33565b601a5550565b6001600160a01b03811660009081526009602052604090206060906121fe90613a62565b60606006805480602002602001604051908101604052809291908181526020018280548015612a3857602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612a1a575b5050505050905090565b60006121fe81836147f0565b601d546001600160a01b03163314612a78576040516282b42960e81b815260040160405180910390fd5b601754601a6000828254612a8c9190615a33565b9091555050565b3a600003612ab457604051630e661aed60e41b815260040160405180910390fd5b6001600160a01b038216612adb5760405163d92e233d60e01b815260040160405180910390fd5b80600003612afc57604051633ca0029d60e01b815260040160405180910390fd5b6001600160a01b0382166000818152600f602090815260408083203380855290835281842054858552601084528285205460138552838620838752855283862054968652601b9094529184205490949193861315612c0657856000612b618988612712565b905080821115612b8e57604051632de4882160e01b815260048101829052602481018390526044016117dc565b612b988287615a33565b9550612ba48286615a33565b9450612bb08285615bab565b9350612bbc8284615bab565b925081601c6000828254612bd09190615bab565b90915550506015546001600160a01b03808b166000908152601160209081526040808320938c168352929052205550612c5c9050565b6000612c1187615d4d565b9050612c1d8186615bab565b9450612c298185615bab565b9350612c358184615a33565b9250612c418183615a33565b915080601c6000828254612c559190615a33565b9091555050505b6001600160a01b038088166000818152600f60209081526040808320948a1680845294825280832089905583835260108252808320889055601382528083208584528252808320879055838352601b90915290208390559003612d2b578115801590612cc95750600c5482105b15612d0157604051636fe84f3d60e01b81526001600160a01b03808916600483015286166024820152604481018790526064016117dc565b6000861315612d205781600003612d1b57612d1b87613998565b612d98565b612d1b876001613938565b8115801590612d3b5750600d5482105b15612d7357604051636fe84f3d60e01b81526001600160a01b03808916600483015286166024820152604481018790526064016117dc565b6000861315612d8e5781600003612d1b57612d1b8786614169565b612d988786614812565b612da1876144f9565b601554856001600160a01b0316886001600160a01b03167f80d5c777e5f7ac6ee89723223803ca5c0ec0204f89e99c1b0cde973c66a6459489604051612de991815260200190565b60405180910390a450505050505050565b604080518082019091526000815260606020820152612e1761471d565b82516020808501919091206001600160e01b0319908116600090815292825260409283902083518085018552815460e01b9092168252600181018054855181860281018601909652808652929491938581019390830182828015612e9a57602002820191906000526020600020905b815481526020019060010190808311612e86575b5050505050815250509050919050565b600080612eb561471d565b6001600160e01b031980861660009081526020838152604080832081518083018352815460e01b90951685526001810180548351818602810186019094528084529697509395909385840193909190830182828015612f3357602002820191906000526020600020905b815481526020019060010190808311612f1f575b5050505050815250509050806020015151600003612f56576000925050506121fe565b60208101518151600090612f699061483f565b905060005b82518110156130775781838281518110612f8a57612f8a615a1d565b60200260200101510361306f576000808211612fc05783600081518110612fb357612fb3615a1d565b6020026020010151612fe5565b83612fcc600184615bab565b81518110612fdc57612fdc615a1d565b60200260200101515b9050600060018551612ff79190615bab565b831061302957846001865161300c9190615bab565b8151811061301c5761301c615a1d565b602002602001015161304e565b84613035846001615a33565b8151811061304557613045615a1d565b60200260200101515b90508189141580156130605750808914155b159750505050505050506121fe565b600101612f6e565b506000979650505050505050565b6040805180820190915260008152606060208201526130a261471d565b6001600160e01b03198084166000908152602092835260409081902081518083018352815460e01b9093168352600181018054835181870281018701909452808452939491938583019392830182828015612e9a5760200282019190600052602060002090815481526020019060010190808311612e86575050505050815250509050919050565b600060175460165460185461313f9190615a33565b6122109190615bab565b6024546001600160a01b03163314613173576040516282b42960e81b815260040160405180910390fd5b601d546040516307b9342f60e21b81526001600160a01b0384811660048301526000921690631ee4d0bc90602401602060405180830381865afa1580156131be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131e29190615c02565b90506131ee8183614916565b505050565b3a60000361321457604051630e661aed60e41b815260040160405180910390fd5b61321c6149c0565b6001600160a01b0381166000908152601160209081526040808320338085529252909120546015541161327557604051630b06352b60e31b81526001600160a01b038084166004830152821660248201526044016117dc565b6001600160a01b038083166000908152600f60209081526040808320938516835292905290812054908190036132be57604051633ca0029d60e01b815260040160405180910390fd5b6001600160a01b038084166000818152600f602090815260408083209487168352938152838220829055918152601090915220546132fd908290615bab565b6001600160a01b0380851660009081526010602090815260408083209490945560138152838220928616825291909152908120549003613341576133418383614a0a565b61334b82826141cb565b601554826001600160a01b0316846001600160a01b03167ff380b0bc887e00f5b50d3c9d4eaaf5c9a0afd97b956316b995159384c4ede9b38460405161339391815260200190565b60405180910390a45050611c5060017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b601d60009054906101000a90046001600160a01b03166001600160a01b03166356b54bae6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613419573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343d9190615c02565b6001600160a01b0316336001600160a01b03161461346d576040516282b42960e81b815260040160405180910390fd5b6000828152602080805260408083206001600160a01b03851684529091528120549003611615576001600160a01b0381166000908152601b6020526040812054908190036134ba57505050565b6000838152602080805260408083206001600160a01b038616808552908352818420949094556013825280832082528083205486845260218352818420948452939091529020555050565b60606122106004613a62565b60606122106002613a62565b3a60000361353e57604051630e661aed60e41b815260040160405180910390fd5b601d546040516253517560e01b81523360048201819052916000916001600160a01b03909116906253517590602401602060405180830381865afa15801561358a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ae9190615c02565b9050601554600014801561362b5750601d5460405163facd743b60e01b81526001600160a01b0383811660048301529091169063facd743b90602401602060405180830381865afa158015613607573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061362b9190615bbe565b15613654576040516303d71f2d60e51b81526001600160a01b03831660048201526024016117dc565b61161582613a76565b601d546001600160a01b03163314613687576040516282b42960e81b815260040160405180910390fd5b6015805490600061369783615d69565b90915550506000601a55565b6136ab6140bb565b6001600160a01b0381166136d557604051631e4fbdf760e01b8152600060048201526024016117dc565b611c508161477f565b3a6000036136ff57604051630e661aed60e41b815260040160405180910390fd5b6137076149c0565b33613713838284613abc565b61371d81836141cb565b601554816001600160a01b0316846001600160a01b03167fa7c0f0cac6bd4d18042007706c84a8abe823751cf289b69c01e83eef7b5915c78560405161376591815260200190565b60405180910390a45061161560017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b60006137a381836147f0565b806121fe57506121fe6002836147f0565b60c081013515806137ce57508061010001358160c0013511155b156137ec5760405163c671116b60e01b815260040160405180910390fd5b806101000135600003613812576040516332154b4760e01b815260040160405180910390fd5b60e0810135158061382b57508060c001358160e0013510155b1561384957604051630685efe960e01b815260040160405180910390fd5b6000613858602083018361542f565b6001600160a01b03160361387f5760405163d92e233d60e01b815260040160405180910390fd5b61388c60408201826159a6565b90506000036138ae57604051635c9a24ed60e11b815260040160405180910390fd5b606081013515806138c157506080810135155b156138ef5760405163633373e160e11b815260808201356004820152606082013560248201526044016117dc565b80608001358160a0013511611c5057604051631c6d3b1560e01b815260040160405180910390fd5b61391f614b60565b611c5081614bae565b613930614b60565b6123a7614bb6565b61394182612a42565b61397d57613950600083614bbe565b50610bb861395e6000614bd3565b111561397d5760405163398dcd9d60e21b815260040160405180910390fd5b613988600283614154565b5080156116155761161582614bdd565b6139a3600482614bbe565b50611c5081614cd2565b6139b5614b60565b60006139bf61471d565b6040805180820182526001600160e01b0319868116825260208083018781529189166000908152858252939093208251815463ffffffff191660e09190911c1781559051805194955091939092613a1d926001850192910190615274565b509050507f3665bf9cd0ba4ddceeec259e21dcf8a4510f3b1130bd42e950828e69d85408ba848484604051613a5493929190615d82565b60405180910390a150505050565b60606000613a6f83614eef565b9392505050565b613a81600082614154565b50613a8b81614116565b15613aa157613a9b600282614154565b50613aaa565b613aaa81614f4a565b613ab381614cd2565b611c5081614f55565b6001600160a01b038316613ae35760405163d92e233d60e01b815260040160405180910390fd5b80600003613b0457604051633ca0029d60e01b815260040160405180910390fd5b6000613b108484612215565b905080821115613b3d57604051632de4882160e01b815260048101829052602481018390526044016117dc565b6001600160a01b038085166000908152601360209081526040808320938716835292905290812054613b70908490615bab565b90506000846001600160a01b0316866001600160a01b031614613b9557600d54613b99565b600c545b90508115801590613ba957508082105b15613be157604051639e612d5760e01b81526001600160a01b03808816600483015286166024820152604481018590526064016117dc565b856001600160a01b0316856001600160a01b031614613c7657601d546040516253517560e01b81526001600160a01b03888116600483015260009216906253517590602401602060405180830381865afa158015613c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c679190615c02565b9050613c74878288614f60565b505b6001600160a01b038681166000818152601360209081526040808320948a16808452948252808320879055928252600b8152828220938252928352818120601554825290925290205484811015613cce576000613cd8565b613cd88582615bab565b6001600160a01b038089166000818152600b60209081526040808320948c1683529381528382206015548352815283822094909455908152601b90925281208054879290613d27908490615bab565b9250508190555084601c6000828254613d409190615bab565b90915550506000839003613d5857613d588787614a0a565b613d61876144f9565b50505050505050565b6001600160a01b038316613d915760405163d92e233d60e01b815260040160405180910390fd5b601d546040516253517560e01b81526001600160a01b03858116600483015260009216906253517590602401602060405180830381865afa158015613dda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dfe9190615c02565b90506001600160a01b038116613e3257604051632670461960e11b81526001600160a01b03851660048201526024016117dc565b81600003613e665760405163e59f5c3960e01b81526001600160a01b038086166004830152841660248201526044016117dc565b6001600160a01b0384166000908152601f602052604090205460ff1615613eab5760405163078137eb60e41b81526001600160a01b03851660048201526024016117dc565b6001600160a01b038085166000818152601360209081526040808320948816808452949091528120549290911491613ee4908590615a33565b905060008215613ef75750600c54613f46565b50600d546001600160a01b03871660009081526013602090815260408083209091528120549003613f465760405163cbbeb0bd60e01b81526001600160a01b03881660048201526024016117dc565b80821015613f7a5760405163e59f5c3960e01b81526001600160a01b038089166004830152871660248201526044016117dc565b600e546001600160a01b0388166000908152601b6020526040902054613fa1908790615a33565b1115613fd357604051632c2b174160e21b81526001600160a01b038089166004830152871660248201526044016117dc565b6001600160a01b038088166000908152600b60209081526040808320938a168352928152828220601554835290529081208054879290614014908490615a33565b90915550506001600160a01b0387166000908152601b602052604081208054879290614041908490615a33565b9250508190555084601c600082825461405a9190615a33565b9091555050821561407557614070876001613938565b61408a565b61407f8787614812565b61408a878588614f60565b6001600160a01b038088166000908152601360209081526040808320938a16835292905220829055613d61876144f9565b336140ed7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146123a75760405163118cdaa760e01b81523360048201526024016117dc565b6001600160a01b0381166000908152601b60205260408120541580156121fe5750506001600160a01b03166000908152601060205260409020541590565b6000613a6f836001600160a01b03841661507c565b6001600160a01b038216600090815260096020526040902061418b9082614154565b506001600160a01b038083166000908152600f6020908152604080832093851683529290522054156141c157611615828261516f565b6116158282615191565b804710156141ec57604051631e9acf1760e31b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614239576040519150601f19603f3d011682016040523d82523d6000602084013e61423e565b606091505b50509050806131ee57604051630e21dcbb60e11b81526001600160a01b0384166004820152602481018390526044016117dc565b61429660405180606001604052806000815260200160008152602001600081525090565b6015546000818152602080805260408083206001600160a01b03891680855290835281842054948452602183528184209084529091528120549060646142dc8688615a06565b6142e69190615be0565b90506142f28187615bab565b6040808601919091526001600160a01b0380891660009081526026602090815283822054602590915292902054161580159061432d57508015155b1561434f5761271061433f8289615a06565b6143499190615be0565b60208601525b838386604001516143609190615a06565b61436a9190615be0565b60208601516143799084615bab565b6143839190615a33565b855250929695505050505050565b6000836000036143a357506000613a6f565b6001600160a01b0380841660009081526023602090815260408083209386168352929052205484900361440457506001600160a01b038083166000908152602260209081526040808320938516835292815282822086835290522054613a6f565b506001600160a01b038083166000908152601360209081526040808320938516835292905220549392505050565b6001600160a01b03808316600090815260256020908152604080832054600990925290912091169061446490826147f0565b614472576144728382614812565b6001600160a01b038084166000908152601360209081526040808320938516835292905290812080548492906144a9908490615a33565b90915550506001600160a01b038084166000908152600b6020908152604080832093851683529281528282206015548352905290812080548492906144ef908490615a33565b9091555050505050565b601d546040516253517560e01b81526001600160a01b03838116600483015260009216906253517590602401602060405180830381865afa158015614542573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145669190615c02565b60248054604051633941e77760e21b81526001600160a01b0380851660048301529394506000939091169163e5079ddc9101602060405180830381865afa1580156145b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145d99190615daf565b90506131ee8382614916565b6107d081111561460b5760405163e429b69160e01b8152600481018290526024016117dc565b6001600160a01b03821615801561462157508015155b156146515760405163073c315960e11b81526001600160a01b0383166004820152602481018290526044016117dc565b6001600160a01b038316600090815260276020526040902054801580159061467a575060155481145b1561469e5760155460405163e7cbc70160e01b81526004016117dc91815260200190565b6001600160a01b03848116600081815260256020908152604080832080546001600160a01b03191695891695861790556026825280832087905560155460278352928190209290925590518581527fd6ae57aa2cc060d4094c47d20c672afe0e53963e6459c15cab215abb8c88b863910160405180910390a350505050565b60008060ff1961474e60017fdace3fd3d1fbdfd33853f19ba191d28c617e373ec58fc73cf7b58db5aff2c2ab615bab565b60405160200161476091815260200190565b60408051601f1981840301815291905280516020909101201692915050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6001600160a01b03811660009081526001830160205260408120541515613a6f565b6001600160a01b03821660009081526009602052604090206148349082614bbe565b506116158282615191565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03198516179052905160009190829081903090614896908590615dc8565b600060405180830381855afa9150503d80600081146148d1576040519150601f19603f3d011682016040523d82523d6000602084013e6148d6565b606091505b5091509150816148f957604051635fbab09b60e11b815260040160405180910390fd5b8080602001905181019061490d9190615daf565b95945050505050565b600080614922846151b3565b91509150816149315750505050565b60006007828154811061494657614946615a1d565b60009182526020808320909101546001600160a01b0388168352601b9091526040822054909250614978908690615a06565b9050806007848154811061498e5761498e615a1d565b906000526020600020018190555080826008546149ab9190615bab565b6149b59190615a33565b600855505050505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00805460011901614a0457604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b816001600160a01b0316816001600160a01b031603614b1757601d546040516253517560e01b81526001600160a01b03848116600483015260009216906253517590602401602060405180830381865afa158015614a6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a909190615c02565b601d5460405163facd743b60e01b81526001600160a01b03808416600483015292935091169063facd743b90602401602060405180830381865afa158015614adc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b009190615bbe565b15614b0e576131ee83613998565b6131ee83613a76565b614b218282614169565b614b2a82614116565b15611615576131ee600283614154565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff166123a757604051631afcd79f60e31b815260040160405180910390fd5b6136ab614b60565b614b3a614b60565b6000613a6f836001600160a01b038416615225565b60006121fe825490565b6001600160a01b0381166000908152601260205260409020546006548082101580614c385750826001600160a01b031660068381548110614c2057614c20615a1d565b6000918252602090912001546001600160a01b031614155b15614cc9576001600160a01b03831660008181526012602052604081208390556006805460018181019092557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b03191690931790925560078054928301815581527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688909101555b6131ee83614f55565b60075460065414614ce05750565b6001600160a01b03811660009081526012602052604090205460065481108015614d395750816001600160a01b031660068281548110614d2257614d22615a1d565b6000918252602090912001546001600160a01b0316145b156116155760078181548110614d5157614d51615a1d565b906000526020600020015460085410614d9e5760078181548110614d7757614d77615a1d565b906000526020600020015460086000828254614d939190615bab565b90915550614da49050565b60006008555b600654600090614db690600190615bab565b9050600060068281548110614dcd57614dcd615a1d565b600091825260209091200154600680546001600160a01b039092169250829185908110614dfc57614dfc615a1d565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060078281548110614e3d57614e3d615a1d565b906000526020600020015460078481548110614e5b57614e5b615a1d565b60009182526020808320909101929092556001600160a01b038084168252601290925260408082208690559186168152908120556006805480614ea057614ea0615de4565b600082815260209020810160001990810180546001600160a01b03191690550190556007805480614ed357614ed3615de4565b6001900381819060005260206000200160009055905550505050565b6060816000018054806020026020016040519081016040528092919081815260200182805480156121ce57602002820191906000526020600020905b815481526020019060010190808311614f2b5750505050509050919050565b611615600282614bbe565b611615600482614154565b601d5460405163a0d16cad60e01b81526001600160a01b0384811660048301529091169063a0d16cad90602401602060405180830381865afa158015614faa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614fce9190615bbe565b1580614fda5750601554155b15614fe457505050565b6001600160a01b03808416600090815260236020908152604080832093851683529290522054601554811015615076576001600160a01b0380851660008181526013602090815260408083209487168084529482528083205484845260228352818420868552835281842060158054865290845282852091909155549383526023825280832094835293905291909120555b50505050565b600081815260018301602052604081205480156151655760006150a0600183615bab565b85549091506000906150b490600190615bab565b90508082146151195760008660000182815481106150d4576150d4615a1d565b90600052602060002001549050808760000184815481106150f7576150f7615a1d565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061512a5761512a615de4565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506121fe565b60009150506121fe565b6001600160a01b0382166000908152600a602052604090206131ee9082614bbe565b6001600160a01b0382166000908152600a602052604090206131ee9082614154565b6001600160a01b0381166000908152601260205260408120546006548110801561520c5750826001600160a01b0316600682815481106151f5576151f5615a1d565b6000918252602090912001546001600160a01b0316145b1561521a5760019150915091565b506000928392509050565b600081815260018301602052604081205461526c575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556121fe565b5060006121fe565b8280548282559060005260206000209081019282156152af579160200282015b828111156152af578251825591602001919060010190615294565b506152bb9291506152d9565b5090565b5080546000825590600052602060002090810190611c5091905b5b808211156152bb57600081556001016152da565b6001600160a01b0381168114611c5057600080fd5b60008083601f84011261531557600080fd5b50813567ffffffffffffffff81111561532d57600080fd5b6020830191508360208260051b850101111561534857600080fd5b9250929050565b6000806000806000806080878903121561536857600080fd5b8635615373816152ee565b9550602087013567ffffffffffffffff8082111561539057600080fd5b90880190610120828b0312156153a557600080fd5b909550604088013590808211156153bb57600080fd5b6153c78a838b01615303565b909650945060608901359150808211156153e057600080fd5b506153ed89828a01615303565b979a9699509497509295939492505050565b6000806040838503121561541257600080fd5b823591506020830135615424816152ee565b809150509250929050565b60006020828403121561544157600080fd5b8135613a6f816152ee565b60008060006060848603121561546157600080fd5b833561546c816152ee565b9250602084013561547c816152ee565b929592945050506040919091013590565b60006020828403121561549f57600080fd5b5035919050565b600080604083850312156154b957600080fd5b82356154c4816152ee565b946020939093013593505050565b80356001600160e01b0319811681146154ea57600080fd5b919050565b6000806000806060858703121561550557600080fd5b61550e856154d2565b935061551c602086016154d2565b9250604085013567ffffffffffffffff81111561553857600080fd5b61554487828801615303565b95989497509550505050565b60005b8381101561556b578181015183820152602001615553565b50506000910152565b6000815180845261558c816020860160208601615550565b601f01601f19169290920160200192915050565b602081526000613a6f6020830184615574565b6020808252825182820181905260009190848201906040850190845b818110156155f45783516001600160a01b0316835292840192918401916001016155cf565b50909695505050505050565b6000806040838503121561561357600080fd5b823561561e816152ee565b91506020830135615424816152ee565b80356fffffffffffffffffffffffffffffffff19811681146154ea57600080fd5b80356001600160f01b0319811681146154ea57600080fd5b60008060006060848603121561567c57600080fd5b8335615687816152ee565b92506156956020850161562e565b91506156a36040850161564f565b90509250925092565b6000602082840312156156be57600080fd5b613a6f826154d2565b60008083601f8401126156d957600080fd5b50813567ffffffffffffffff8111156156f157600080fd5b60208301915083602082850101111561534857600080fd5b6000806000806060858703121561571f57600080fd5b843567ffffffffffffffff81111561573657600080fd5b615742878288016156c7565b909550935061575590506020860161562e565b91506157636040860161564f565b905092959194509250565b60008060008060008060a0878903121561578757600080fd5b8635615792816152ee565b955060208701356157a2816152ee565b945060408701359350606087013567ffffffffffffffff8111156157c557600080fd5b6157d189828a016156c7565b90945092506157e490506080880161562e565b90509295509295509295565b60008151808452602080850194506020840160005b8381101561582157815187529582019590820190600101615805565b509495945050505050565b60408152600061583f60408301856157f0565b90508260208301529392505050565b6060815260006158616060830186615574565b6fffffffffffffffffffffffffffffffff19949094166020830152506001600160f01b031991909116604090910152919050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156158bd57600080fd5b813567ffffffffffffffff808211156158d557600080fd5b818401915084601f8301126158e957600080fd5b8135818111156158fb576158fb615895565b604051601f8201601f19908116603f0116810190838211818310171561592357615923615895565b8160405282815287602084870101111561593c57600080fd5b826020860160208301376000928101602001929092525095945050505050565b602080825282516001600160e01b0319168282015282015160408083015260009061285260608401826157f0565b6000806040838503121561599d57600080fd5b6154c4836154d2565b6000808335601e198436030181126159bd57600080fd5b83018035915067ffffffffffffffff8211156159d857600080fd5b6020019150600581901b360382131561534857600080fd5b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176121fe576121fe6159f0565b634e487b7160e01b600052603260045260246000fd5b808201808211156121fe576121fe6159f0565b600181811c90821680615a5a57607f821691505b602082108103615a7a57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156131ee576000816000526020600020601f850160051c81016020861015615aa95750805b601f850160051c820191505b81811015615ac857828155600101615ab5565b505050505050565b815167ffffffffffffffff811115615aea57615aea615895565b615afe81615af88454615a46565b84615a80565b602080601f831160018114615b335760008415615b1b5750858301515b600019600386901b1c1916600185901b178555615ac8565b600085815260208120601f198616915b82811015615b6257888601518255948401946001909101908401615b43565b5085821015615b805787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215615ba257600080fd5b613a6f8261562e565b818103818111156121fe576121fe6159f0565b600060208284031215615bd057600080fd5b81518015158114613a6f57600080fd5b600082615bfd57634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615c1457600080fd5b8151613a6f816152ee565b6001600160e01b0319858116825284166020820152606060408201819052810182905260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115615c7157600080fd5b8260051b808560808501379190910160800195945050505050565b67ffffffffffffffff831115615ca457615ca4615895565b615cb883615cb28354615a46565b83615a80565b6000601f841160018114615cec5760008515615cd45750838201355b600019600387901b1c1916600186901b178355615d46565b600083815260209020601f19861690835b82811015615d1d5786850135825560209485019460019092019101615cfd565b5086821015615d3a5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6000600160ff1b8201615d6257615d626159f0565b5060000390565b600060018201615d7b57615d7b6159f0565b5060010190565b6001600160e01b031984811682528316602082015260606040820181905260009061490d908301846157f0565b600060208284031215615dc157600080fd5b5051919050565b60008251615dda818460208701615550565b9190910192915050565b634e487b7160e01b600052603160045260246000fdfea26469706673582212201e8e54a31c16c621fa88c77c6ff088a6e5e931cf9c5ea285dbbc5f542d912b4564736f6c63430008190033

Deployed ByteCode

0x60806040526004361061055c5760003560e01c8063862be287116102ca578063c6912cc011610179578063e9ab0300116100d6578063f3fef3a31161008a578063f6c578611161006f578063f6c5786114610f96578063f9d4d54414610fb6578063fa4aaf4714610fe357600080fd5b8063f3fef3a314610f62578063f494250114610f8257600080fd5b8063ee435f55116100bb578063ee435f5514610f17578063f078609614610f2c578063f2fde38b14610f4257600080fd5b8063e9ab030014610eca578063edd7db7514610f0257600080fd5b8063d5c4b08a1161012d578063dadee88a11610112578063dadee88a14610e68578063df6f55f514610e95578063dfc8bf4e14610eaa57600080fd5b8063d5c4b08a14610e3a578063da7a9b6a14610e5257600080fd5b8063cfef14411161015e578063cfef144114610de5578063d0ac76f614610e05578063d290c21d14610e2557600080fd5b8063c6912cc014610db0578063c6af311a14610dc557600080fd5b8063a420596711610227578063ae1aaf80116101db578063b710c15d116101c0578063b710c15d14610d43578063ba08d23714610d70578063bf348f8614610d9057600080fd5b8063ae1aaf8014610d0d578063b61ed63a14610d2357600080fd5b8063a697ecff1161020c578063a697ecff14610ca0578063a711e6a114610cd8578063adddc0cf14610cf857600080fd5b8063a420596714610c53578063a5d54f6514610c8b57600080fd5b80639a7b5f111161027e5780639e6c2959116102635780639e6c295914610bfd5780639e72c63514610c135780639ea8082b14610c3357600080fd5b80639a7b5f1114610bb85780639b03d74414610be757600080fd5b8063921e274b116102af578063921e274b14610b62578063950a651314610b75578063957950a714610b9557600080fd5b8063862be28714610b055780638da5cb5b14610b2557600080fd5b80634f9a8d8f11610426578063715018a6116103835780637b0a0f9b116103375780637d8149db1161031c5780637d8149db14610a985780638247a23914610ab857806384725c7614610ad857600080fd5b80637b0a0f9b14610a585780637b15b94c14610a7857600080fd5b806373c218031161036857806373c21803146109d2578063750dd2a1146109f2578063794c0c6814610a4257600080fd5b8063715018a614610987578063728345db1461099c57600080fd5b80635fef7643116103da578063673a2a1f116103bf578063673a2a1f1461092f5780636bda1577146109515780637069e7461461097157600080fd5b80635fef7643146108e3578063615f2b71146108f957600080fd5b8063567e98f91161040b578063567e98f9146108975780635b4eafe0146108ad5780635d80ca32146108cd57600080fd5b80634f9a8d8f146108545780635267e1d61461086a57600080fd5b806326476204116104d45780633b7d0946116104885780634160d3861161046d5780634160d386146107e75780634346845f146108075780634e9b426d1461082757600080fd5b80633b7d0946146107b45780633f3afe01146107d457600080fd5b80633219d600116104b95780633219d600146107495780633715426314610789578063379b046a1461079e57600080fd5b806326476204146107165780632bafde8d1461072957600080fd5b80631555371c1161052b57806320c07cd81161051057806320c07cd8146106cb57806322e3d986146106e1578063251441a9146106f657600080fd5b80631555371c1461063a5780631fb31e431461064f57600080fd5b8063028b8bdb1461057f5780630ac6e291146105a85780630b770cdf146105ca5780631345b8a51461060257600080fd5b3661057a57604051631574f9f360e01b815260040160405180910390fd5b600080fd5b34801561058b57600080fd5b5061059560145481565b6040519081526020015b60405180910390f35b3480156105b457600080fd5b506105c86105c336600461534f565b611010565b005b3480156105d657600080fd5b506024546105ea906001600160a01b031681565b6040516001600160a01b03909116815260200161059f565b34801561060e57600080fd5b5061059561061d3660046153ff565b602160209081526000928352604080842090915290825290205481565b34801561064657600080fd5b506105c86115a7565b34801561065b57600080fd5b5061069961066a36600461542f565b6001600160a01b03166000908152601e6020526040902060010154608081901b91600160801b90910460f01b90565b604080516fffffffffffffffffffffffffffffffff1990931683526001600160f01b031990911660208301520161059f565b3480156106d757600080fd5b506105956107d081565b3480156106ed57600080fd5b50610595611619565b34801561070257600080fd5b506105c861071136600461544c565b611660565b6105c861072436600461542f565b61171e565b34801561073557600080fd5b506105c861074436600461548d565b6117a1565b34801561075557600080fd5b5061077961076436600461542f565b601f6020526000908152604090205460ff1681565b604051901515815260200161059f565b34801561079557600080fd5b506105c8611822565b3480156107aa57600080fd5b50610595601a5481565b3480156107c057600080fd5b506105c86107cf36600461542f565b611c1d565b6105c86107e23660046154a6565b611c53565b3480156107f357600080fd5b506105c86108023660046154a6565b611f89565b34801561081357600080fd5b506105c86108223660046154ef565b612028565b34801561083357600080fd5b5061084761084236600461542f565b61212e565b60405161059f91906155a0565b34801561086057600080fd5b5061059560165481565b34801561087657600080fd5b5061059561088536600461542f565b601b6020526000908152604090205481565b3480156108a357600080fd5b50610595601c5481565b3480156108b957600080fd5b506105956108c83660046153ff565b6121da565b3480156108d957600080fd5b50610595600e5481565b3480156108ef57600080fd5b50610595600c5481565b34801561090557600080fd5b506105ea61091436600461542f565b6025602052600090815260409020546001600160a01b031681565b34801561093b57600080fd5b50610944612204565b60405161059f91906155b3565b34801561095d57600080fd5b5061059561096c366004615600565b612215565b34801561097d57600080fd5b5061059560195481565b34801561099357600080fd5b506105c8612395565b3480156109a857600080fd5b506105956109b73660046153ff565b60208080526000928352604080842090915290825290205481565b3480156109de57600080fd5b506109446109ed36600461542f565b6123a9565b3480156109fe57600080fd5b50610595610a0d366004615600565b6001600160a01b039182166000908152600b602090815260408083209390941682529182528281206015548252909152205490565b348015610a4e57600080fd5b5061059560155481565b348015610a6457600080fd5b506105c8610a73366004615667565b6123cd565b348015610a8457600080fd5b506105c8610a9336600461542f565b61244a565b348015610aa457600080fd5b506105c8610ab33660046156ac565b6124b1565b348015610ac457600080fd5b506105c8610ad336600461548d565b612533565b348015610ae457600080fd5b50610595610af336600461542f565b60266020526000908152604090205481565b348015610b1157600080fd5b506105c8610b20366004615709565b612566565b348015610b3157600080fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166105ea565b6105c8610b7036600461576e565b6125c9565b348015610b8157600080fd5b50610595610b90366004615600565b612712565b348015610ba157600080fd5b50610baa61285a565b60405161059f92919061582c565b348015610bc457600080fd5b50610bd8610bd336600461542f565b6128bd565b60405161059f9392919061584e565b348015610bf357600080fd5b5061059560185481565b348015610c0957600080fd5b5061059561271081565b348015610c1f57600080fd5b506105c8610c2e36600461548d565b612971565b348015610c3f57600080fd5b50610944610c4e36600461542f565b6129bc565b348015610c5f57600080fd5b50610595610c6e366004615600565b601160209081526000928352604080842090915290825290205481565b348015610c9757600080fd5b506109446129e0565b348015610cac57600080fd5b50610595610cbb366004615600565b601360209081526000928352604080842090915290825290205481565b348015610ce457600080fd5b50610779610cf336600461542f565b612a42565b348015610d0457600080fd5b506105c8612a4e565b348015610d1957600080fd5b5061059560175481565b348015610d2f57600080fd5b506105c8610d3e3660046154a6565b612a93565b348015610d4f57600080fd5b50610d63610d5e3660046158ab565b612dfa565b60405161059f919061595c565b348015610d7c57600080fd5b50610779610d8b36600461598a565b612eaa565b348015610d9c57600080fd5b50610d63610dab3660046156ac565b613085565b348015610dbc57600080fd5b5061059561312a565b348015610dd157600080fd5b506105c8610de03660046154a6565b613149565b348015610df157600080fd5b506105c8610e0036600461542f565b6131f3565b348015610e1157600080fd5b506105c8610e203660046153ff565b6133c6565b348015610e3157600080fd5b50610944613505565b348015610e4657600080fd5b506105c8610384601755565b348015610e5e57600080fd5b50610595600d5481565b348015610e7457600080fd5b50610595610e8336600461542f565b60106020526000908152604090205481565b348015610ea157600080fd5b50610944613511565b348015610eb657600080fd5b50601d546105ea906001600160a01b031681565b348015610ed657600080fd5b50610595610ee5366004615600565b600f60209081526000928352604080842090915290825290205481565b348015610f0e57600080fd5b506105c861351d565b348015610f2357600080fd5b506105c861365d565b348015610f3857600080fd5b50610595610bb881565b348015610f4e57600080fd5b506105c8610f5d36600461542f565b6136a3565b348015610f6e57600080fd5b506105c8610f7d3660046154a6565b6136de565b348015610f8e57600080fd5b506001610779565b348015610fa257600080fd5b50610779610fb136600461542f565b613797565b348015610fc257600080fd5b50610595610fd136600461542f565b60276020526000908152604090205481565b348015610fef57600080fd5b50610595610ffe36600461542f565b60126020526000908152604090205481565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561105b5750825b905060008267ffffffffffffffff1660011480156110785750303b155b905081158015611086575080155b156110a45760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156110d857845468ff00000000000000001916680100000000000000001785555b6001600160a01b038b166110ff5760405163d92e233d60e01b815260040160405180910390fd5b6111088a6137b4565b8761111660408c018c6159a6565b61112291506002615a06565b1461113f5760405162d6988760e81b815260040160405180910390fd5b8561114d60408c018c6159a6565b90501461116d576040516320041afd60e01b815260040160405180910390fd5b6111768b613917565b61117e613928565b61118b60208b018b61542f565b601d80546001600160a01b0319166001600160a01b03929092169190911790556111bb60408b0160208c0161542f565b602480546001600160a01b0319166001600160a01b03929092169190911790553660006111eb60408d018d6159a6565b9150915060005b8181101561140e57600083838381811061120e5761120e615a1d565b9050602002016020810190611223919061542f565b6001600160a01b03160361124a5760405163d92e233d60e01b815260040160405180910390fd5b61127b83838381811061125f5761125f615a1d565b9050602002016020810190611274919061542f565b6000613938565b6112aa83838381811061129057611290615a1d565b90506020020160208101906112a5919061542f565b613998565b8b8b6112b7836002615a06565b8181106112c6576112c6615a1d565b905060200201358c8c8360026112dc9190615a06565b6112e7906001615a33565b8181106112f6576112f6615a1d565b90506020020135604051602001611317929190918252602082015260400190565b604051602081830303815290604052601e600085858581811061133c5761133c615a1d565b9050602002016020810190611351919061542f565b6001600160a01b031681526020810191909152604001600020906113759082615ad0565b5089898281811061138857611388615a1d565b905060200201602081019061139d9190615b90565b601e60008585858181106113b3576113b3615a1d565b90506020020160208101906113c8919061542f565b6001600160a01b031681526020810191909152604001600020600190810180546fffffffffffffffffffffffffffffffff191660809390931c92909217909155016111f2565b5060408051600580825260c082019092526000916020820160a0803683370190505090506802b5e3af16b18800008160008151811061144f5761144f615a1d565b60200260200101818152505068056bc75e2d631000008160018151811061147857611478615a1d565b602002602001018181525050680821ab0d4414980000816002815181106114a1576114a1615a1d565b602002602001018181525050680ad78ebc5ac6200000816003815181106114ca576114ca615a1d565b602002602001018181525050680d8d726b7177a80000816004815181106114f3576114f3615a1d565b6020908102919091010152611517632bafde8d60e01b636d3d4db560e11b836139ad565b50505060608a0135600d5560808a0135600c5560a08a0135600e5560c08a01356016556101008a01356014554260185560e08a0135601755831561159a57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050505050565b601d546001600160a01b031633146115d1576040516282b42960e81b815260040160405180910390fd5b60006115dd6004613a62565b905060005b81518110156116155761160d82828151811061160057611600615a1d565b6020026020010151613a76565b6001016115e2565b5050565b601854601654600091901561162f576001611632565b60005b60ff16601a54601654836116469190615a33565b6116509190615a33565b61165a9190615bab565b91505090565b3a60000361168157604051630e661aed60e41b815260040160405180910390fd5b816001600160a01b0316836001600160a01b0316036116b357604051630a5eddd560e01b815260040160405180910390fd5b336116bf848284613abc565b6116ca838284613d6a565b601554604080516001600160a01b0387811682526020820186905280851692908716917f4480d8e4b1e9095b94bf513961d26fe1d32386ebdd103d18fe8738cf4b2223ff910160405180910390a450505050565b3a60000361173f57604051630e661aed60e41b815260040160405180910390fd5b333461174c838383613d6a565b601554826001600160a01b0316846001600160a01b03167f2273de02cb1f69ba6259d22c4bc22c60e4c94c193265ef6afee324a04a9b6d228460405161179491815260200190565b60405180910390a4505050565b6117a96140bb565b806117c06000356001600160e01b03191682612eaa565b6117e5576040516373330d9b60e01b8152600481018290526024015b60405180910390fd5b600d8290556040518281527ffee02ce7aa40f9c49eaabd26d404fa88714b97cb209af8954cfd5eeb8213b93e906020015b60405180910390a15050565b3a60000361184357604051630e661aed60e41b815260040160405180910390fd5b6000806118506002613a62565b9050805160000361187457604051631c369b4560e21b815260040160405180910390fd5b60005b8151811015611a5c57600082828151811061189457611894615a1d565b602002602001015190506118a781614116565b8061191d5750601d54604051639d6fc1d160e01b81526001600160a01b03838116600483015290911690639d6fc1d190602401602060405180830381865afa1580156118f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191b9190615bbe565b155b156119285750611a54565b611933600282614154565b506001600160a01b0381166000908152601f60209081526040808320805460ff19166001179055601b9091528120805490829055601c80549192839261197a908490615bab565b909155506000905061198b836129bc565b905060005b81518110156119f65760008282815181106119ad576119ad615a1d565b6020908102919091018101516001600160a01b03808816600090815260138452604080822092841682529190935282209190915590506119ed8582614169565b50600101611990565b50611a018287615a33565b9550826001600160a01b0316336001600160a01b03167f8e6a4ccd7dccdca9ac211d00fcc3fa7c71be75ff73d3a35f63ea023173cc100484604051611a4891815260200190565b60405180910390a35050505b600101611877565b5081600003611a7e57604051631c369b4560e21b815260040160405180910390fd5b6000611a8b600284615be0565b90506000611a998285615bab565b90506000601d60009054906101000a90046001600160a01b03166001600160a01b03166356b54bae6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611af0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b149190615c02565b90506000816001600160a01b031663732524946040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b7a9190615c02565b9050816001600160a01b031663af182535846040518263ffffffff1660e01b81526004016000604051808303818588803b158015611bb757600080fd5b505af1158015611bcb573d6000803e3d6000fd5b5050505050611bda81856141cb565b604080518481526020810186905233917f1c113e6bae9530fea40323e612aeb0cb7817dedb5a0b0f6bdfbff97d55920dc7910160405180910390a2505050505050565b601d546001600160a01b03163314611c47576040516282b42960e81b815260040160405180910390fd5b611c5081613a76565b50565b601d60009054906101000a90046001600160a01b03166001600160a01b03166356b54bae6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ca6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cca9190615c02565b6001600160a01b0316336001600160a01b031614611cfa576040516282b42960e81b815260040160405180910390fd5b3415611615576015546000908152602080805260408083206001600160a01b03861684529091528120543491611d31858486614272565b90506000611d3e866129bc565b905060005b8151811015611e8d57600084611d756015548a868681518110611d6857611d68615a1d565b6020026020010151614391565b8560400151611d849190615a06565b611d8e9190615be0565b905080601360008a6001600160a01b03166001600160a01b031681526020019081526020016000206000858581518110611dca57611dca615a1d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000828254611e019190615a33565b90915550506001600160a01b0388166000908152600b602052604081208451839290869086908110611e3557611e35615a1d565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600060155481526020019081526020016000206000828254611e7f9190615a33565b909155505050600101611d43565b50602082015115611ea657611ea6868360200151614432565b81516001600160a01b038716600090815260136020908152604080832090915281208054909190611ed8908490615a33565b90915550506001600160a01b0386166000908152601b602052604081208054869290611f05908490615a33565b9250508190555083601c6000828254611f1e9190615a33565b90915550611f2d9050866144f9565b60155482516001600160a01b038816907f72093068b9f28053bd924ac15d7710b987f9b6ef1e0f89f47d8b4bd7cac776dc90611f698189615bab565b6040805192835260208301919091520160405180910390a3505050505050565b601d546040516253517560e01b81523360048201526000916001600160a01b0316906253517590602401602060405180830381865afa158015611fd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ff49190615c02565b6001600160a01b03160361201d57604051632670461960e11b81523360048201526024016117dc565b6116153383836145e5565b6120306140bb565b600061203a61471d565b90506040518060400160405280857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200184848080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509390945250506001600160e01b031988168152602084815260409091208351815463ffffffff191660e09190911c1781558382015180519193506120e6926001850192910190615274565b509050507f3665bf9cd0ba4ddceeec259e21dcf8a4510f3b1130bd42e950828e69d85408ba8585858560405161211f9493929190615c1f565b60405180910390a15050505050565b6001600160a01b0381166000908152601e6020526040902080546060919061215590615a46565b80601f016020809104026020016040519081016040528092919081815260200182805461218190615a46565b80156121ce5780601f106121a3576101008083540402835291602001916121ce565b820191906000526020600020905b8154815290600101906020018083116121b157829003601f168201915b50505050509050919050565b60008281526021602090815260408083206001600160a01b03851684529091529020545b92915050565b60606122106000613a62565b905090565b601d546040516253517560e01b81526001600160a01b03848116600483015260009283929116906253517590602401602060405180830381865afa158015612261573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122859190615c02565b90506001600160a01b0384166000908152601f602052604090205460ff16156122b25760009150506121fe565b6001600160a01b03848116600090815260136020908152604080832087851684529091529081902054601d54915163a0d16cad60e01b815284841660048201529092919091169063a0d16cad90602401602060405180830381865afa15801561231f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123439190615bbe565b6123505791506121fe9050565b6001600160a01b038581166000908152600b6020908152604080832093881683529281528282206015548352905220548082111561238c578091505b50949350505050565b61239d6140bb565b6123a7600061477f565b565b6001600160a01b0381166000908152600a602052604090206060906121fe90613a62565b601d546001600160a01b031633146123f7576040516282b42960e81b815260040160405180910390fd5b6001600160a01b039092166000908152601e60205260409020600101805460f09390931c600160801b0271ffffffffffffffffffffffffffffffffffff1990931660809290921c91909117919091179055565b601d546001600160a01b03163314612474576040516282b42960e81b815260040160405180910390fd5b600c546001600160a01b038216600090815260136020908152604080832090915290205410611c50576124a8816001613938565b611c50816144f9565b6124b96140bb565b60006124c361471d565b6001600160e01b031983166000908152602082905260408120805463ffffffff191681559192506124f760018301826152bf565b50506040516001600160e01b0319831681527fed27cb02231782dadf13473a7828cb980c4d685791b7a3136dde00f8c3594cb690602001611816565b601d546001600160a01b0316331461255d576040516282b42960e81b815260040160405180910390fd5b60185543601955565b336000908152601e60205260409020612580848683615c8c565b50336000908152601e60205260409020600101805460f09290921c600160801b0271ffffffffffffffffffffffffffffffffffff1990921660809390931c929092171790555050565b3a6000036125ea57604051630e661aed60e41b815260040160405180910390fd5b601d54604051630526083960e11b81526001600160a01b0388811660048301523360248301819052923492911690630a4c107290604401600060405180830381600087803b15801561263b57600080fd5b505af115801561264f573d6000803e3d6000fd5b505050506001600160a01b0382166000908152601e60205260409020612676858783615c8c565b506001600160a01b0382166000908152601e6020526040902060010180546fffffffffffffffffffffffffffffffff1916608085901c1790556126ba8288886145e5565b6126c5828383613d6a565b6015546040518281526001600160a01b0384169081907f2273de02cb1f69ba6259d22c4bc22c60e4c94c193265ef6afee324a04a9b6d229060200160405180910390a45050505050505050565b601d546040516253517560e01b81526001600160a01b03848116600483015260009283929116906253517590602401602060405180830381865afa15801561275e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127829190615c02565b9050601d5460405163a0d16cad60e01b81526001600160a01b0383811660048301529091169063a0d16cad90602401602060405180830381865afa1580156127ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f29190615bbe565b6128005760009150506121fe565b6001600160a01b038481166000818152600b6020908152604080832094881680845294825280832060155484528252808320549383526013825280832094835293905291909120546128529190615bab565b949350505050565b606060006007600854818054806020026020016040519081016040528092919081815260200182805480156128ae57602002820191906000526020600020905b81548152602001906001019080831161289a575b50505050509150915091509091565b601e602052600090815260409020805481906128d890615a46565b80601f016020809104026020016040519081016040528092919081815260200182805461290490615a46565b80156129515780601f1061292657610100808354040283529160200191612951565b820191906000526020600020905b81548152906001019060200180831161293457829003601f168201915b50505060019093015491925050608081901b90600160801b900460f01b83565b601d546001600160a01b0316331461299b576040516282b42960e81b815260040160405180910390fd5b60175481601a546129ac9190615a33565b6129b69190615a33565b601a5550565b6001600160a01b03811660009081526009602052604090206060906121fe90613a62565b60606006805480602002602001604051908101604052809291908181526020018280548015612a3857602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612a1a575b5050505050905090565b60006121fe81836147f0565b601d546001600160a01b03163314612a78576040516282b42960e81b815260040160405180910390fd5b601754601a6000828254612a8c9190615a33565b9091555050565b3a600003612ab457604051630e661aed60e41b815260040160405180910390fd5b6001600160a01b038216612adb5760405163d92e233d60e01b815260040160405180910390fd5b80600003612afc57604051633ca0029d60e01b815260040160405180910390fd5b6001600160a01b0382166000818152600f602090815260408083203380855290835281842054858552601084528285205460138552838620838752855283862054968652601b9094529184205490949193861315612c0657856000612b618988612712565b905080821115612b8e57604051632de4882160e01b815260048101829052602481018390526044016117dc565b612b988287615a33565b9550612ba48286615a33565b9450612bb08285615bab565b9350612bbc8284615bab565b925081601c6000828254612bd09190615bab565b90915550506015546001600160a01b03808b166000908152601160209081526040808320938c168352929052205550612c5c9050565b6000612c1187615d4d565b9050612c1d8186615bab565b9450612c298185615bab565b9350612c358184615a33565b9250612c418183615a33565b915080601c6000828254612c559190615a33565b9091555050505b6001600160a01b038088166000818152600f60209081526040808320948a1680845294825280832089905583835260108252808320889055601382528083208584528252808320879055838352601b90915290208390559003612d2b578115801590612cc95750600c5482105b15612d0157604051636fe84f3d60e01b81526001600160a01b03808916600483015286166024820152604481018790526064016117dc565b6000861315612d205781600003612d1b57612d1b87613998565b612d98565b612d1b876001613938565b8115801590612d3b5750600d5482105b15612d7357604051636fe84f3d60e01b81526001600160a01b03808916600483015286166024820152604481018790526064016117dc565b6000861315612d8e5781600003612d1b57612d1b8786614169565b612d988786614812565b612da1876144f9565b601554856001600160a01b0316886001600160a01b03167f80d5c777e5f7ac6ee89723223803ca5c0ec0204f89e99c1b0cde973c66a6459489604051612de991815260200190565b60405180910390a450505050505050565b604080518082019091526000815260606020820152612e1761471d565b82516020808501919091206001600160e01b0319908116600090815292825260409283902083518085018552815460e01b9092168252600181018054855181860281018601909652808652929491938581019390830182828015612e9a57602002820191906000526020600020905b815481526020019060010190808311612e86575b5050505050815250509050919050565b600080612eb561471d565b6001600160e01b031980861660009081526020838152604080832081518083018352815460e01b90951685526001810180548351818602810186019094528084529697509395909385840193909190830182828015612f3357602002820191906000526020600020905b815481526020019060010190808311612f1f575b5050505050815250509050806020015151600003612f56576000925050506121fe565b60208101518151600090612f699061483f565b905060005b82518110156130775781838281518110612f8a57612f8a615a1d565b60200260200101510361306f576000808211612fc05783600081518110612fb357612fb3615a1d565b6020026020010151612fe5565b83612fcc600184615bab565b81518110612fdc57612fdc615a1d565b60200260200101515b9050600060018551612ff79190615bab565b831061302957846001865161300c9190615bab565b8151811061301c5761301c615a1d565b602002602001015161304e565b84613035846001615a33565b8151811061304557613045615a1d565b60200260200101515b90508189141580156130605750808914155b159750505050505050506121fe565b600101612f6e565b506000979650505050505050565b6040805180820190915260008152606060208201526130a261471d565b6001600160e01b03198084166000908152602092835260409081902081518083018352815460e01b9093168352600181018054835181870281018701909452808452939491938583019392830182828015612e9a5760200282019190600052602060002090815481526020019060010190808311612e86575050505050815250509050919050565b600060175460165460185461313f9190615a33565b6122109190615bab565b6024546001600160a01b03163314613173576040516282b42960e81b815260040160405180910390fd5b601d546040516307b9342f60e21b81526001600160a01b0384811660048301526000921690631ee4d0bc90602401602060405180830381865afa1580156131be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131e29190615c02565b90506131ee8183614916565b505050565b3a60000361321457604051630e661aed60e41b815260040160405180910390fd5b61321c6149c0565b6001600160a01b0381166000908152601160209081526040808320338085529252909120546015541161327557604051630b06352b60e31b81526001600160a01b038084166004830152821660248201526044016117dc565b6001600160a01b038083166000908152600f60209081526040808320938516835292905290812054908190036132be57604051633ca0029d60e01b815260040160405180910390fd5b6001600160a01b038084166000818152600f602090815260408083209487168352938152838220829055918152601090915220546132fd908290615bab565b6001600160a01b0380851660009081526010602090815260408083209490945560138152838220928616825291909152908120549003613341576133418383614a0a565b61334b82826141cb565b601554826001600160a01b0316846001600160a01b03167ff380b0bc887e00f5b50d3c9d4eaaf5c9a0afd97b956316b995159384c4ede9b38460405161339391815260200190565b60405180910390a45050611c5060017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b601d60009054906101000a90046001600160a01b03166001600160a01b03166356b54bae6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613419573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343d9190615c02565b6001600160a01b0316336001600160a01b03161461346d576040516282b42960e81b815260040160405180910390fd5b6000828152602080805260408083206001600160a01b03851684529091528120549003611615576001600160a01b0381166000908152601b6020526040812054908190036134ba57505050565b6000838152602080805260408083206001600160a01b038616808552908352818420949094556013825280832082528083205486845260218352818420948452939091529020555050565b60606122106004613a62565b60606122106002613a62565b3a60000361353e57604051630e661aed60e41b815260040160405180910390fd5b601d546040516253517560e01b81523360048201819052916000916001600160a01b03909116906253517590602401602060405180830381865afa15801561358a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ae9190615c02565b9050601554600014801561362b5750601d5460405163facd743b60e01b81526001600160a01b0383811660048301529091169063facd743b90602401602060405180830381865afa158015613607573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061362b9190615bbe565b15613654576040516303d71f2d60e51b81526001600160a01b03831660048201526024016117dc565b61161582613a76565b601d546001600160a01b03163314613687576040516282b42960e81b815260040160405180910390fd5b6015805490600061369783615d69565b90915550506000601a55565b6136ab6140bb565b6001600160a01b0381166136d557604051631e4fbdf760e01b8152600060048201526024016117dc565b611c508161477f565b3a6000036136ff57604051630e661aed60e41b815260040160405180910390fd5b6137076149c0565b33613713838284613abc565b61371d81836141cb565b601554816001600160a01b0316846001600160a01b03167fa7c0f0cac6bd4d18042007706c84a8abe823751cf289b69c01e83eef7b5915c78560405161376591815260200190565b60405180910390a45061161560017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b60006137a381836147f0565b806121fe57506121fe6002836147f0565b60c081013515806137ce57508061010001358160c0013511155b156137ec5760405163c671116b60e01b815260040160405180910390fd5b806101000135600003613812576040516332154b4760e01b815260040160405180910390fd5b60e0810135158061382b57508060c001358160e0013510155b1561384957604051630685efe960e01b815260040160405180910390fd5b6000613858602083018361542f565b6001600160a01b03160361387f5760405163d92e233d60e01b815260040160405180910390fd5b61388c60408201826159a6565b90506000036138ae57604051635c9a24ed60e11b815260040160405180910390fd5b606081013515806138c157506080810135155b156138ef5760405163633373e160e11b815260808201356004820152606082013560248201526044016117dc565b80608001358160a0013511611c5057604051631c6d3b1560e01b815260040160405180910390fd5b61391f614b60565b611c5081614bae565b613930614b60565b6123a7614bb6565b61394182612a42565b61397d57613950600083614bbe565b50610bb861395e6000614bd3565b111561397d5760405163398dcd9d60e21b815260040160405180910390fd5b613988600283614154565b5080156116155761161582614bdd565b6139a3600482614bbe565b50611c5081614cd2565b6139b5614b60565b60006139bf61471d565b6040805180820182526001600160e01b0319868116825260208083018781529189166000908152858252939093208251815463ffffffff191660e09190911c1781559051805194955091939092613a1d926001850192910190615274565b509050507f3665bf9cd0ba4ddceeec259e21dcf8a4510f3b1130bd42e950828e69d85408ba848484604051613a5493929190615d82565b60405180910390a150505050565b60606000613a6f83614eef565b9392505050565b613a81600082614154565b50613a8b81614116565b15613aa157613a9b600282614154565b50613aaa565b613aaa81614f4a565b613ab381614cd2565b611c5081614f55565b6001600160a01b038316613ae35760405163d92e233d60e01b815260040160405180910390fd5b80600003613b0457604051633ca0029d60e01b815260040160405180910390fd5b6000613b108484612215565b905080821115613b3d57604051632de4882160e01b815260048101829052602481018390526044016117dc565b6001600160a01b038085166000908152601360209081526040808320938716835292905290812054613b70908490615bab565b90506000846001600160a01b0316866001600160a01b031614613b9557600d54613b99565b600c545b90508115801590613ba957508082105b15613be157604051639e612d5760e01b81526001600160a01b03808816600483015286166024820152604481018590526064016117dc565b856001600160a01b0316856001600160a01b031614613c7657601d546040516253517560e01b81526001600160a01b03888116600483015260009216906253517590602401602060405180830381865afa158015613c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c679190615c02565b9050613c74878288614f60565b505b6001600160a01b038681166000818152601360209081526040808320948a16808452948252808320879055928252600b8152828220938252928352818120601554825290925290205484811015613cce576000613cd8565b613cd88582615bab565b6001600160a01b038089166000818152600b60209081526040808320948c1683529381528382206015548352815283822094909455908152601b90925281208054879290613d27908490615bab565b9250508190555084601c6000828254613d409190615bab565b90915550506000839003613d5857613d588787614a0a565b613d61876144f9565b50505050505050565b6001600160a01b038316613d915760405163d92e233d60e01b815260040160405180910390fd5b601d546040516253517560e01b81526001600160a01b03858116600483015260009216906253517590602401602060405180830381865afa158015613dda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dfe9190615c02565b90506001600160a01b038116613e3257604051632670461960e11b81526001600160a01b03851660048201526024016117dc565b81600003613e665760405163e59f5c3960e01b81526001600160a01b038086166004830152841660248201526044016117dc565b6001600160a01b0384166000908152601f602052604090205460ff1615613eab5760405163078137eb60e41b81526001600160a01b03851660048201526024016117dc565b6001600160a01b038085166000818152601360209081526040808320948816808452949091528120549290911491613ee4908590615a33565b905060008215613ef75750600c54613f46565b50600d546001600160a01b03871660009081526013602090815260408083209091528120549003613f465760405163cbbeb0bd60e01b81526001600160a01b03881660048201526024016117dc565b80821015613f7a5760405163e59f5c3960e01b81526001600160a01b038089166004830152871660248201526044016117dc565b600e546001600160a01b0388166000908152601b6020526040902054613fa1908790615a33565b1115613fd357604051632c2b174160e21b81526001600160a01b038089166004830152871660248201526044016117dc565b6001600160a01b038088166000908152600b60209081526040808320938a168352928152828220601554835290529081208054879290614014908490615a33565b90915550506001600160a01b0387166000908152601b602052604081208054879290614041908490615a33565b9250508190555084601c600082825461405a9190615a33565b9091555050821561407557614070876001613938565b61408a565b61407f8787614812565b61408a878588614f60565b6001600160a01b038088166000908152601360209081526040808320938a16835292905220829055613d61876144f9565b336140ed7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146123a75760405163118cdaa760e01b81523360048201526024016117dc565b6001600160a01b0381166000908152601b60205260408120541580156121fe5750506001600160a01b03166000908152601060205260409020541590565b6000613a6f836001600160a01b03841661507c565b6001600160a01b038216600090815260096020526040902061418b9082614154565b506001600160a01b038083166000908152600f6020908152604080832093851683529290522054156141c157611615828261516f565b6116158282615191565b804710156141ec57604051631e9acf1760e31b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614239576040519150601f19603f3d011682016040523d82523d6000602084013e61423e565b606091505b50509050806131ee57604051630e21dcbb60e11b81526001600160a01b0384166004820152602481018390526044016117dc565b61429660405180606001604052806000815260200160008152602001600081525090565b6015546000818152602080805260408083206001600160a01b03891680855290835281842054948452602183528184209084529091528120549060646142dc8688615a06565b6142e69190615be0565b90506142f28187615bab565b6040808601919091526001600160a01b0380891660009081526026602090815283822054602590915292902054161580159061432d57508015155b1561434f5761271061433f8289615a06565b6143499190615be0565b60208601525b838386604001516143609190615a06565b61436a9190615be0565b60208601516143799084615bab565b6143839190615a33565b855250929695505050505050565b6000836000036143a357506000613a6f565b6001600160a01b0380841660009081526023602090815260408083209386168352929052205484900361440457506001600160a01b038083166000908152602260209081526040808320938516835292815282822086835290522054613a6f565b506001600160a01b038083166000908152601360209081526040808320938516835292905220549392505050565b6001600160a01b03808316600090815260256020908152604080832054600990925290912091169061446490826147f0565b614472576144728382614812565b6001600160a01b038084166000908152601360209081526040808320938516835292905290812080548492906144a9908490615a33565b90915550506001600160a01b038084166000908152600b6020908152604080832093851683529281528282206015548352905290812080548492906144ef908490615a33565b9091555050505050565b601d546040516253517560e01b81526001600160a01b03838116600483015260009216906253517590602401602060405180830381865afa158015614542573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145669190615c02565b60248054604051633941e77760e21b81526001600160a01b0380851660048301529394506000939091169163e5079ddc9101602060405180830381865afa1580156145b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145d99190615daf565b90506131ee8382614916565b6107d081111561460b5760405163e429b69160e01b8152600481018290526024016117dc565b6001600160a01b03821615801561462157508015155b156146515760405163073c315960e11b81526001600160a01b0383166004820152602481018290526044016117dc565b6001600160a01b038316600090815260276020526040902054801580159061467a575060155481145b1561469e5760155460405163e7cbc70160e01b81526004016117dc91815260200190565b6001600160a01b03848116600081815260256020908152604080832080546001600160a01b03191695891695861790556026825280832087905560155460278352928190209290925590518581527fd6ae57aa2cc060d4094c47d20c672afe0e53963e6459c15cab215abb8c88b863910160405180910390a350505050565b60008060ff1961474e60017fdace3fd3d1fbdfd33853f19ba191d28c617e373ec58fc73cf7b58db5aff2c2ab615bab565b60405160200161476091815260200190565b60408051601f1981840301815291905280516020909101201692915050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6001600160a01b03811660009081526001830160205260408120541515613a6f565b6001600160a01b03821660009081526009602052604090206148349082614bbe565b506116158282615191565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03198516179052905160009190829081903090614896908590615dc8565b600060405180830381855afa9150503d80600081146148d1576040519150601f19603f3d011682016040523d82523d6000602084013e6148d6565b606091505b5091509150816148f957604051635fbab09b60e11b815260040160405180910390fd5b8080602001905181019061490d9190615daf565b95945050505050565b600080614922846151b3565b91509150816149315750505050565b60006007828154811061494657614946615a1d565b60009182526020808320909101546001600160a01b0388168352601b9091526040822054909250614978908690615a06565b9050806007848154811061498e5761498e615a1d565b906000526020600020018190555080826008546149ab9190615bab565b6149b59190615a33565b600855505050505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00805460011901614a0457604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b816001600160a01b0316816001600160a01b031603614b1757601d546040516253517560e01b81526001600160a01b03848116600483015260009216906253517590602401602060405180830381865afa158015614a6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a909190615c02565b601d5460405163facd743b60e01b81526001600160a01b03808416600483015292935091169063facd743b90602401602060405180830381865afa158015614adc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b009190615bbe565b15614b0e576131ee83613998565b6131ee83613a76565b614b218282614169565b614b2a82614116565b15611615576131ee600283614154565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff166123a757604051631afcd79f60e31b815260040160405180910390fd5b6136ab614b60565b614b3a614b60565b6000613a6f836001600160a01b038416615225565b60006121fe825490565b6001600160a01b0381166000908152601260205260409020546006548082101580614c385750826001600160a01b031660068381548110614c2057614c20615a1d565b6000918252602090912001546001600160a01b031614155b15614cc9576001600160a01b03831660008181526012602052604081208390556006805460018181019092557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b03191690931790925560078054928301815581527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688909101555b6131ee83614f55565b60075460065414614ce05750565b6001600160a01b03811660009081526012602052604090205460065481108015614d395750816001600160a01b031660068281548110614d2257614d22615a1d565b6000918252602090912001546001600160a01b0316145b156116155760078181548110614d5157614d51615a1d565b906000526020600020015460085410614d9e5760078181548110614d7757614d77615a1d565b906000526020600020015460086000828254614d939190615bab565b90915550614da49050565b60006008555b600654600090614db690600190615bab565b9050600060068281548110614dcd57614dcd615a1d565b600091825260209091200154600680546001600160a01b039092169250829185908110614dfc57614dfc615a1d565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060078281548110614e3d57614e3d615a1d565b906000526020600020015460078481548110614e5b57614e5b615a1d565b60009182526020808320909101929092556001600160a01b038084168252601290925260408082208690559186168152908120556006805480614ea057614ea0615de4565b600082815260209020810160001990810180546001600160a01b03191690550190556007805480614ed357614ed3615de4565b6001900381819060005260206000200160009055905550505050565b6060816000018054806020026020016040519081016040528092919081815260200182805480156121ce57602002820191906000526020600020905b815481526020019060010190808311614f2b5750505050509050919050565b611615600282614bbe565b611615600482614154565b601d5460405163a0d16cad60e01b81526001600160a01b0384811660048301529091169063a0d16cad90602401602060405180830381865afa158015614faa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614fce9190615bbe565b1580614fda5750601554155b15614fe457505050565b6001600160a01b03808416600090815260236020908152604080832093851683529290522054601554811015615076576001600160a01b0380851660008181526013602090815260408083209487168084529482528083205484845260228352818420868552835281842060158054865290845282852091909155549383526023825280832094835293905291909120555b50505050565b600081815260018301602052604081205480156151655760006150a0600183615bab565b85549091506000906150b490600190615bab565b90508082146151195760008660000182815481106150d4576150d4615a1d565b90600052602060002001549050808760000184815481106150f7576150f7615a1d565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061512a5761512a615de4565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506121fe565b60009150506121fe565b6001600160a01b0382166000908152600a602052604090206131ee9082614bbe565b6001600160a01b0382166000908152600a602052604090206131ee9082614154565b6001600160a01b0381166000908152601260205260408120546006548110801561520c5750826001600160a01b0316600682815481106151f5576151f5615a1d565b6000918252602090912001546001600160a01b0316145b1561521a5760019150915091565b506000928392509050565b600081815260018301602052604081205461526c575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556121fe565b5060006121fe565b8280548282559060005260206000209081019282156152af579160200282015b828111156152af578251825591602001919060010190615294565b506152bb9291506152d9565b5090565b5080546000825590600052602060002090810190611c5091905b5b808211156152bb57600081556001016152da565b6001600160a01b0381168114611c5057600080fd5b60008083601f84011261531557600080fd5b50813567ffffffffffffffff81111561532d57600080fd5b6020830191508360208260051b850101111561534857600080fd5b9250929050565b6000806000806000806080878903121561536857600080fd5b8635615373816152ee565b9550602087013567ffffffffffffffff8082111561539057600080fd5b90880190610120828b0312156153a557600080fd5b909550604088013590808211156153bb57600080fd5b6153c78a838b01615303565b909650945060608901359150808211156153e057600080fd5b506153ed89828a01615303565b979a9699509497509295939492505050565b6000806040838503121561541257600080fd5b823591506020830135615424816152ee565b809150509250929050565b60006020828403121561544157600080fd5b8135613a6f816152ee565b60008060006060848603121561546157600080fd5b833561546c816152ee565b9250602084013561547c816152ee565b929592945050506040919091013590565b60006020828403121561549f57600080fd5b5035919050565b600080604083850312156154b957600080fd5b82356154c4816152ee565b946020939093013593505050565b80356001600160e01b0319811681146154ea57600080fd5b919050565b6000806000806060858703121561550557600080fd5b61550e856154d2565b935061551c602086016154d2565b9250604085013567ffffffffffffffff81111561553857600080fd5b61554487828801615303565b95989497509550505050565b60005b8381101561556b578181015183820152602001615553565b50506000910152565b6000815180845261558c816020860160208601615550565b601f01601f19169290920160200192915050565b602081526000613a6f6020830184615574565b6020808252825182820181905260009190848201906040850190845b818110156155f45783516001600160a01b0316835292840192918401916001016155cf565b50909695505050505050565b6000806040838503121561561357600080fd5b823561561e816152ee565b91506020830135615424816152ee565b80356fffffffffffffffffffffffffffffffff19811681146154ea57600080fd5b80356001600160f01b0319811681146154ea57600080fd5b60008060006060848603121561567c57600080fd5b8335615687816152ee565b92506156956020850161562e565b91506156a36040850161564f565b90509250925092565b6000602082840312156156be57600080fd5b613a6f826154d2565b60008083601f8401126156d957600080fd5b50813567ffffffffffffffff8111156156f157600080fd5b60208301915083602082850101111561534857600080fd5b6000806000806060858703121561571f57600080fd5b843567ffffffffffffffff81111561573657600080fd5b615742878288016156c7565b909550935061575590506020860161562e565b91506157636040860161564f565b905092959194509250565b60008060008060008060a0878903121561578757600080fd5b8635615792816152ee565b955060208701356157a2816152ee565b945060408701359350606087013567ffffffffffffffff8111156157c557600080fd5b6157d189828a016156c7565b90945092506157e490506080880161562e565b90509295509295509295565b60008151808452602080850194506020840160005b8381101561582157815187529582019590820190600101615805565b509495945050505050565b60408152600061583f60408301856157f0565b90508260208301529392505050565b6060815260006158616060830186615574565b6fffffffffffffffffffffffffffffffff19949094166020830152506001600160f01b031991909116604090910152919050565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156158bd57600080fd5b813567ffffffffffffffff808211156158d557600080fd5b818401915084601f8301126158e957600080fd5b8135818111156158fb576158fb615895565b604051601f8201601f19908116603f0116810190838211818310171561592357615923615895565b8160405282815287602084870101111561593c57600080fd5b826020860160208301376000928101602001929092525095945050505050565b602080825282516001600160e01b0319168282015282015160408083015260009061285260608401826157f0565b6000806040838503121561599d57600080fd5b6154c4836154d2565b6000808335601e198436030181126159bd57600080fd5b83018035915067ffffffffffffffff8211156159d857600080fd5b6020019150600581901b360382131561534857600080fd5b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176121fe576121fe6159f0565b634e487b7160e01b600052603260045260246000fd5b808201808211156121fe576121fe6159f0565b600181811c90821680615a5a57607f821691505b602082108103615a7a57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156131ee576000816000526020600020601f850160051c81016020861015615aa95750805b601f850160051c820191505b81811015615ac857828155600101615ab5565b505050505050565b815167ffffffffffffffff811115615aea57615aea615895565b615afe81615af88454615a46565b84615a80565b602080601f831160018114615b335760008415615b1b5750858301515b600019600386901b1c1916600185901b178555615ac8565b600085815260208120601f198616915b82811015615b6257888601518255948401946001909101908401615b43565b5085821015615b805787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215615ba257600080fd5b613a6f8261562e565b818103818111156121fe576121fe6159f0565b600060208284031215615bd057600080fd5b81518015158114613a6f57600080fd5b600082615bfd57634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615c1457600080fd5b8151613a6f816152ee565b6001600160e01b0319858116825284166020820152606060408201819052810182905260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115615c7157600080fd5b8260051b808560808501379190910160800195945050505050565b67ffffffffffffffff831115615ca457615ca4615895565b615cb883615cb28354615a46565b83615a80565b6000601f841160018114615cec5760008515615cd45750838201355b600019600387901b1c1916600186901b178355615d46565b600083815260209020601f19861690835b82811015615d1d5786850135825560209485019460019092019101615cfd565b5086821015615d3a5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6000600160ff1b8201615d6257615d626159f0565b5060000390565b600060018201615d7b57615d7b6159f0565b5060010190565b6001600160e01b031984811682528316602082015260606040820181905260009061490d908301846157f0565b600060208284031215615dc157600080fd5b5051919050565b60008251615dda818460208701615550565b9190910192915050565b634e487b7160e01b600052603160045260246000fdfea26469706673582212201e8e54a31c16c621fa88c77c6ff088a6e5e931cf9c5ea285dbbc5f542d912b4564736f6c63430008190033