Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- StakingRouter
- Optimization enabled
- true
- Compiler version
- v0.8.9+commit.e5eed63a
- Optimization runs
- 150
- EVM Version
- istanbul
- Verified at
- 2024-06-07T12:44:20.286831Z
Constructor Arguments
0x0000000000000000000000003693693693693693693693693693693693693693
Arg [0] (address) : 0x3693693693693693693693693693693693693693
contracts/0.8.9/StakingRouter.sol
// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
/* See contracts/COMPILERS.md */
pragma solidity 0.8.9;
import {AccessControlEnumerable} from "./utils/access/AccessControlEnumerable.sol";
import {IStakingModule} from "./interfaces/IStakingModule.sol";
import {Math256} from "../common/lib/Math256.sol";
import {UnstructuredStorage} from "./lib/UnstructuredStorage.sol";
import {MinFirstAllocationStrategy} from "../common/lib/MinFirstAllocationStrategy.sol";
import {BeaconChainDepositor} from "./BeaconChainDepositor.sol";
import {Versioned} from "./utils/Versioned.sol";
contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Versioned {
using UnstructuredStorage for bytes32;
/// @dev events
event StakingModuleAdded(uint256 indexed stakingModuleId, address stakingModule, string name, address createdBy);
event StakingModuleTargetShareSet(uint256 indexed stakingModuleId, uint256 targetShare, address setBy);
event StakingModuleFeesSet(uint256 indexed stakingModuleId, uint256 stakingModuleFee, uint256 treasuryFee, address setBy);
event StakingModuleStatusSet(uint256 indexed stakingModuleId, StakingModuleStatus status, address setBy);
event StakingModuleExitedValidatorsIncompleteReporting(uint256 indexed stakingModuleId, uint256 unreportedExitedValidatorsCount);
event WithdrawalCredentialsSet(bytes32 withdrawalCredentials, address setBy);
event WithdrawalsCredentialsChangeFailed(uint256 indexed stakingModuleId, bytes lowLevelRevertData);
event ExitedAndStuckValidatorsCountsUpdateFailed(uint256 indexed stakingModuleId, bytes lowLevelRevertData);
event RewardsMintedReportFailed(uint256 indexed stakingModuleId, bytes lowLevelRevertData);
/// Emitted when the StakingRouter received ETH
event StakingRouterETHDeposited(uint256 indexed stakingModuleId, uint256 amount);
/// @dev errors
error ZeroAddress(string field);
error ValueOver100Percent(string field);
error StakingModuleNotActive();
error StakingModuleNotPaused();
error EmptyWithdrawalsCredentials();
error DirectETHTransfer();
error InvalidReportData(uint256 code);
error ExitedValidatorsCountCannotDecrease();
error ReportedExitedValidatorsExceedDeposited(
uint256 reportedExitedValidatorsCount,
uint256 depositedValidatorsCount
);
error StakingModulesLimitExceeded();
error StakingModuleUnregistered();
error AppAuthLidoFailed();
error StakingModuleStatusTheSame();
error StakingModuleWrongName();
error UnexpectedCurrentValidatorsCount(
uint256 currentModuleExitedValidatorsCount,
uint256 currentNodeOpExitedValidatorsCount,
uint256 currentNodeOpStuckValidatorsCount
);
error InvalidDepositsValue(uint256 etherValue, uint256 depositsCount);
error StakingModuleAddressExists();
error ArraysLengthMismatch(uint256 firstArrayLength, uint256 secondArrayLength);
error UnrecoverableModuleError();
enum StakingModuleStatus {
Active, // deposits and rewards allowed
DepositsPaused, // deposits NOT allowed, rewards allowed
Stopped // deposits and rewards NOT allowed
}
struct StakingModule {
/// @notice unique id of the staking module
uint24 id;
/// @notice address of staking module
address stakingModuleAddress;
/// @notice part of the fee taken from staking rewards that goes to the staking module
uint16 stakingModuleFee;
/// @notice part of the fee taken from staking rewards that goes to the treasury
uint16 treasuryFee;
/// @notice target percent of total validators in protocol, in BP
uint16 targetShare;
/// @notice staking module status if staking module can not accept the deposits or can participate in further reward distribution
uint8 status;
/// @notice name of staking module
string name;
/// @notice block.timestamp of the last deposit of the staking module
/// @dev NB: lastDepositAt gets updated even if the deposit value was 0 and no actual deposit happened
uint64 lastDepositAt;
/// @notice block.number of the last deposit of the staking module
/// @dev NB: lastDepositBlock gets updated even if the deposit value was 0 and no actual deposit happened
uint256 lastDepositBlock;
/// @notice number of exited validators
uint256 exitedValidatorsCount;
}
struct StakingModuleCache {
address stakingModuleAddress;
uint24 stakingModuleId;
uint16 stakingModuleFee;
uint16 treasuryFee;
uint16 targetShare;
StakingModuleStatus status;
uint256 activeValidatorsCount;
uint256 availableValidatorsCount;
}
bytes32 public constant MANAGE_WITHDRAWAL_CREDENTIALS_ROLE = keccak256("MANAGE_WITHDRAWAL_CREDENTIALS_ROLE");
bytes32 public constant STAKING_MODULE_PAUSE_ROLE = keccak256("STAKING_MODULE_PAUSE_ROLE");
bytes32 public constant STAKING_MODULE_RESUME_ROLE = keccak256("STAKING_MODULE_RESUME_ROLE");
bytes32 public constant STAKING_MODULE_MANAGE_ROLE = keccak256("STAKING_MODULE_MANAGE_ROLE");
bytes32 public constant REPORT_EXITED_VALIDATORS_ROLE = keccak256("REPORT_EXITED_VALIDATORS_ROLE");
bytes32 public constant UNSAFE_SET_EXITED_VALIDATORS_ROLE = keccak256("UNSAFE_SET_EXITED_VALIDATORS_ROLE");
bytes32 public constant REPORT_REWARDS_MINTED_ROLE = keccak256("REPORT_REWARDS_MINTED_ROLE");
bytes32 internal constant LIDO_POSITION = keccak256("lido.StakingRouter.lido");
/// @dev Credentials which allows the DAO to withdraw Ether on the 2.0 side
bytes32 internal constant WITHDRAWAL_CREDENTIALS_POSITION = keccak256("lido.StakingRouter.withdrawalCredentials");
/// @dev total count of staking modules
bytes32 internal constant STAKING_MODULES_COUNT_POSITION = keccak256("lido.StakingRouter.stakingModulesCount");
/// @dev id of the last added staking module. This counter grow on staking modules adding
bytes32 internal constant LAST_STAKING_MODULE_ID_POSITION = keccak256("lido.StakingRouter.lastStakingModuleId");
/// @dev mapping is used instead of array to allow to extend the StakingModule
bytes32 internal constant STAKING_MODULES_MAPPING_POSITION = keccak256("lido.StakingRouter.stakingModules");
/// @dev Position of the staking modules in the `_stakingModules` map, plus 1 because
/// index 0 means a value is not in the set.
bytes32 internal constant STAKING_MODULE_INDICES_MAPPING_POSITION = keccak256("lido.StakingRouter.stakingModuleIndicesOneBased");
uint256 public constant FEE_PRECISION_POINTS = 10 ** 20; // 100 * 10 ** 18
uint256 public constant TOTAL_BASIS_POINTS = 10000;
uint256 public constant MAX_STAKING_MODULES_COUNT = 32;
/// @dev restrict the name size with 31 bytes to storage in a single slot
uint256 public constant MAX_STAKING_MODULE_NAME_LENGTH = 31;
constructor(address _depositContract) BeaconChainDepositor(_depositContract) {}
/**
* @dev proxy initialization
* @param _admin Lido DAO Aragon agent contract address
* @param _lido Lido address
* @param _withdrawalCredentials Lido withdrawal vault contract address
*/
function initialize(address _admin, address _lido, bytes32 _withdrawalCredentials) external {
if (_admin == address(0)) revert ZeroAddress("_admin");
if (_lido == address(0)) revert ZeroAddress("_lido");
_initializeContractVersionTo(1);
_setupRole(DEFAULT_ADMIN_ROLE, _admin);
LIDO_POSITION.setStorageAddress(_lido);
WITHDRAWAL_CREDENTIALS_POSITION.setStorageBytes32(_withdrawalCredentials);
emit WithdrawalCredentialsSet(_withdrawalCredentials, msg.sender);
}
/// @dev prohibit direct transfer to contract
receive() external payable {
revert DirectETHTransfer();
}
/**
* @notice Return the Lido contract address
*/
function getLido() public view returns (address) {
return LIDO_POSITION.getStorageAddress();
}
/**
* @notice register a new staking module
* @param _name name of staking module
* @param _stakingModuleAddress address of staking module
* @param _targetShare target total stake share
* @param _stakingModuleFee fee of the staking module taken from the consensus layer rewards
* @param _treasuryFee treasury fee
*/
function addStakingModule(
string calldata _name,
address _stakingModuleAddress,
uint256 _targetShare,
uint256 _stakingModuleFee,
uint256 _treasuryFee
) external onlyRole(STAKING_MODULE_MANAGE_ROLE) {
if (_targetShare > TOTAL_BASIS_POINTS)
revert ValueOver100Percent("_targetShare");
if (_stakingModuleFee + _treasuryFee > TOTAL_BASIS_POINTS)
revert ValueOver100Percent("_stakingModuleFee + _treasuryFee");
if (_stakingModuleAddress == address(0))
revert ZeroAddress("_stakingModuleAddress");
if (bytes(_name).length == 0 || bytes(_name).length > MAX_STAKING_MODULE_NAME_LENGTH)
revert StakingModuleWrongName();
uint256 newStakingModuleIndex = getStakingModulesCount();
if (newStakingModuleIndex >= MAX_STAKING_MODULES_COUNT)
revert StakingModulesLimitExceeded();
for (uint256 i; i < newStakingModuleIndex; ) {
if (_stakingModuleAddress == _getStakingModuleByIndex(i).stakingModuleAddress)
revert StakingModuleAddressExists();
unchecked {
++i;
}
}
StakingModule storage newStakingModule = _getStakingModuleByIndex(newStakingModuleIndex);
uint24 newStakingModuleId = uint24(LAST_STAKING_MODULE_ID_POSITION.getStorageUint256()) + 1;
newStakingModule.id = newStakingModuleId;
newStakingModule.name = _name;
newStakingModule.stakingModuleAddress = _stakingModuleAddress;
newStakingModule.targetShare = uint16(_targetShare);
newStakingModule.stakingModuleFee = uint16(_stakingModuleFee);
newStakingModule.treasuryFee = uint16(_treasuryFee);
/// @dev since `enum` is `uint8` by nature, so the `status` is stored as `uint8` to avoid
/// possible problems when upgrading. But for human readability, we use `enum` as
/// function parameter type. More about conversion in the docs
/// https://docs.soliditylang.org/en/v0.8.17/types.html#enums
newStakingModule.status = uint8(StakingModuleStatus.Active);
/// @dev Simulate zero value deposit to prevent real deposits into the new StakingModule via
/// DepositSecurityModule just after the addition.
/// See DepositSecurityModule.getMaxDeposits() for details
newStakingModule.lastDepositAt = uint64(block.timestamp);
newStakingModule.lastDepositBlock = block.number;
emit StakingRouterETHDeposited(newStakingModuleId, 0);
_setStakingModuleIndexById(newStakingModuleId, newStakingModuleIndex);
LAST_STAKING_MODULE_ID_POSITION.setStorageUint256(newStakingModuleId);
STAKING_MODULES_COUNT_POSITION.setStorageUint256(newStakingModuleIndex + 1);
emit StakingModuleAdded(newStakingModuleId, _stakingModuleAddress, _name, msg.sender);
emit StakingModuleTargetShareSet(newStakingModuleId, _targetShare, msg.sender);
emit StakingModuleFeesSet(newStakingModuleId, _stakingModuleFee, _treasuryFee, msg.sender);
}
/**
* @notice Update staking module params
* @param _stakingModuleId staking module id
* @param _targetShare target total stake share
* @param _stakingModuleFee fee of the staking module taken from the consensus layer rewards
* @param _treasuryFee treasury fee
*/
function updateStakingModule(
uint256 _stakingModuleId,
uint256 _targetShare,
uint256 _stakingModuleFee,
uint256 _treasuryFee
) external onlyRole(STAKING_MODULE_MANAGE_ROLE) {
if (_targetShare > TOTAL_BASIS_POINTS) revert ValueOver100Percent("_targetShare");
if (_stakingModuleFee + _treasuryFee > TOTAL_BASIS_POINTS) revert ValueOver100Percent("_stakingModuleFee + _treasuryFee");
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
stakingModule.targetShare = uint16(_targetShare);
stakingModule.treasuryFee = uint16(_treasuryFee);
stakingModule.stakingModuleFee = uint16(_stakingModuleFee);
emit StakingModuleTargetShareSet(_stakingModuleId, _targetShare, msg.sender);
emit StakingModuleFeesSet(_stakingModuleId, _stakingModuleFee, _treasuryFee, msg.sender);
}
/// @notice Updates the limit of the validators that can be used for deposit
/// @param _stakingModuleId Id of the staking module
/// @param _nodeOperatorId Id of the node operator
/// @param _isTargetLimitActive Active flag
/// @param _targetLimit Target limit of the node operator
function updateTargetValidatorsLimits(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
bool _isTargetLimitActive,
uint256 _targetLimit
) external onlyRole(STAKING_MODULE_MANAGE_ROLE) {
address moduleAddr = _getStakingModuleById(_stakingModuleId).stakingModuleAddress;
IStakingModule(moduleAddr)
.updateTargetValidatorsLimits(_nodeOperatorId, _isTargetLimitActive, _targetLimit);
}
/// @notice Updates the number of the refunded validators in the staking module with the given
/// node operator id
/// @param _stakingModuleId Id of the staking module
/// @param _nodeOperatorId Id of the node operator
/// @param _refundedValidatorsCount New number of refunded validators of the node operator
function updateRefundedValidatorsCount(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
uint256 _refundedValidatorsCount
) external onlyRole(STAKING_MODULE_MANAGE_ROLE) {
address moduleAddr = _getStakingModuleById(_stakingModuleId).stakingModuleAddress;
IStakingModule(moduleAddr)
.updateRefundedValidatorsCount(_nodeOperatorId, _refundedValidatorsCount);
}
function reportRewardsMinted(uint256[] calldata _stakingModuleIds, uint256[] calldata _totalShares)
external
onlyRole(REPORT_REWARDS_MINTED_ROLE)
{
if (_stakingModuleIds.length != _totalShares.length) {
revert ArraysLengthMismatch(_stakingModuleIds.length, _totalShares.length);
}
for (uint256 i = 0; i < _stakingModuleIds.length; ) {
if (_totalShares[i] > 0) {
address moduleAddr = _getStakingModuleById(_stakingModuleIds[i]).stakingModuleAddress;
try IStakingModule(moduleAddr).onRewardsMinted(_totalShares[i]) {}
catch (bytes memory lowLevelRevertData) {
/// @dev This check is required to prevent incorrect gas estimation of the method.
/// Without it, Ethereum nodes that use binary search for gas estimation may
/// return an invalid value when the onRewardsMinted() reverts because of the
/// "out of gas" error. Here we assume that the onRewardsMinted() method doesn't
/// have reverts with empty error data except "out of gas".
if (lowLevelRevertData.length == 0) revert UnrecoverableModuleError();
emit RewardsMintedReportFailed(
_stakingModuleIds[i],
lowLevelRevertData
);
}
}
unchecked { ++i; }
}
}
/// @notice Updates total numbers of exited validators for staking modules with the specified
/// module ids.
///
/// @param _stakingModuleIds Ids of the staking modules to be updated.
/// @param _exitedValidatorsCounts New counts of exited validators for the specified staking modules.
///
/// @return The total increase in the aggregate number of exited validators across all updated modules.
///
/// The total numbers are stored in the staking router and can differ from the totals obtained by calling
/// `IStakingModule.getStakingModuleSummary()`. The overall process of updating validator counts is the following:
///
/// 1. In the first data submission phase, the oracle calls `updateExitedValidatorsCountByStakingModule` on the
/// staking router, passing the totals by module. The staking router stores these totals and uses them to
/// distribute new stake and staking fees between the modules. There can only be single call of this function
/// per oracle reporting frame.
///
/// 2. In the first part of the second data submission phase, the oracle calls
/// `StakingRouter.reportStakingModuleStuckValidatorsCountByNodeOperator` on the staking router which passes the
/// counts by node operator to the staking module by calling `IStakingModule.updateStuckValidatorsCount`.
/// This can be done multiple times for the same module, passing data for different subsets of node operators.
///
/// 3. In the second part of the second data submission phase, the oracle calls
/// `StakingRouter.reportStakingModuleExitedValidatorsCountByNodeOperator` on the staking router which passes
/// the counts by node operator to the staking module by calling `IStakingModule.updateExitedValidatorsCount`.
/// This can be done multiple times for the same module, passing data for different subsets of node
/// operators.
///
/// 4. At the end of the second data submission phase, it's expected for the aggregate exited validators count
/// across all module's node operators (stored in the module) to match the total count for this module
/// (stored in the staking router). However, it might happen that the second phase of data submission doesn't
/// finish until the new oracle reporting frame is started, in which case staking router will emit a warning
/// event `StakingModuleExitedValidatorsIncompleteReporting` when the first data submission phase is performed
/// for a new reporting frame. This condition will result in the staking module having an incomplete data about
/// the exited and maybe stuck validator counts during the whole reporting frame. Handling this condition is
/// the responsibility of each staking module.
///
/// 5. When the second reporting phase is finished, i.e. when the oracle submitted the complete data on the stuck
/// and exited validator counts per node operator for the current reporting frame, the oracle calls
/// `StakingRouter.onValidatorsCountsByNodeOperatorReportingFinished` which, in turn, calls
/// `IStakingModule.onExitedAndStuckValidatorsCountsUpdated` on all modules.
///
function updateExitedValidatorsCountByStakingModule(
uint256[] calldata _stakingModuleIds,
uint256[] calldata _exitedValidatorsCounts
)
external
onlyRole(REPORT_EXITED_VALIDATORS_ROLE)
returns (uint256)
{
if (_stakingModuleIds.length != _exitedValidatorsCounts.length) {
revert ArraysLengthMismatch(_stakingModuleIds.length, _exitedValidatorsCounts.length);
}
uint256 newlyExitedValidatorsCount;
for (uint256 i = 0; i < _stakingModuleIds.length; ) {
uint256 stakingModuleId = _stakingModuleIds[i];
StakingModule storage stakingModule = _getStakingModuleById(stakingModuleId);
uint256 prevReportedExitedValidatorsCount = stakingModule.exitedValidatorsCount;
if (_exitedValidatorsCounts[i] < prevReportedExitedValidatorsCount) {
revert ExitedValidatorsCountCannotDecrease();
}
(
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
/* uint256 depositableValidatorsCount */
) = IStakingModule(stakingModule.stakingModuleAddress).getStakingModuleSummary();
if (_exitedValidatorsCounts[i] > totalDepositedValidators) {
revert ReportedExitedValidatorsExceedDeposited(
_exitedValidatorsCounts[i],
totalDepositedValidators
);
}
newlyExitedValidatorsCount += _exitedValidatorsCounts[i] - prevReportedExitedValidatorsCount;
if (totalExitedValidators < prevReportedExitedValidatorsCount) {
// not all of the exited validators were async reported to the module
emit StakingModuleExitedValidatorsIncompleteReporting(
stakingModuleId,
prevReportedExitedValidatorsCount - totalExitedValidators
);
}
stakingModule.exitedValidatorsCount = _exitedValidatorsCounts[i];
unchecked { ++i; }
}
return newlyExitedValidatorsCount;
}
/// @notice Updates exited validators counts per node operator for the staking module with
/// the specified id.
///
/// See the docs for `updateExitedValidatorsCountByStakingModule` for the description of the
/// overall update process.
///
/// @param _stakingModuleId The id of the staking modules to be updated.
/// @param _nodeOperatorIds Ids of the node operators to be updated.
/// @param _exitedValidatorsCounts New counts of exited validators for the specified node operators.
///
function reportStakingModuleExitedValidatorsCountByNodeOperator(
uint256 _stakingModuleId,
bytes calldata _nodeOperatorIds,
bytes calldata _exitedValidatorsCounts
)
external
onlyRole(REPORT_EXITED_VALIDATORS_ROLE)
{
address moduleAddr = _getStakingModuleById(_stakingModuleId).stakingModuleAddress;
_checkValidatorsByNodeOperatorReportData(_nodeOperatorIds, _exitedValidatorsCounts);
IStakingModule(moduleAddr).updateExitedValidatorsCount(
_nodeOperatorIds,
_exitedValidatorsCounts
);
}
struct ValidatorsCountsCorrection {
/// @notice The expected current number of exited validators of the module that is
/// being corrected.
uint256 currentModuleExitedValidatorsCount;
/// @notice The expected current number of exited validators of the node operator
/// that is being corrected.
uint256 currentNodeOperatorExitedValidatorsCount;
/// @notice The expected current number of stuck validators of the node operator
/// that is being corrected.
uint256 currentNodeOperatorStuckValidatorsCount;
/// @notice The corrected number of exited validators of the module.
uint256 newModuleExitedValidatorsCount;
/// @notice The corrected number of exited validators of the node operator.
uint256 newNodeOperatorExitedValidatorsCount;
/// @notice The corrected number of stuck validators of the node operator.
uint256 newNodeOperatorStuckValidatorsCount;
}
/**
* @notice Sets exited validators count for the given module and given node operator in that
* module without performing critical safety checks, e.g. that exited validators count cannot
* decrease.
*
* Should only be used by the DAO in extreme cases and with sufficient precautions to correct
* invalid data reported by the oracle committee due to a bug in the oracle daemon.
*
* @param _stakingModuleId ID of the staking module.
*
* @param _nodeOperatorId ID of the node operator.
*
* @param _triggerUpdateFinish Whether to call `onExitedAndStuckValidatorsCountsUpdated` on
* the module after applying the corrections.
*
* @param _correction See the docs for the `ValidatorsCountsCorrection` struct.
*
* Reverts if the current numbers of exited and stuck validators of the module and node operator
* don't match the supplied expected current values.
*/
function unsafeSetExitedValidatorsCount(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
bool _triggerUpdateFinish,
ValidatorsCountsCorrection memory _correction
)
external
onlyRole(UNSAFE_SET_EXITED_VALIDATORS_ROLE)
{
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
address moduleAddr = stakingModule.stakingModuleAddress;
(
/* bool isTargetLimitActive */,
/* uint256 targetValidatorsCount */,
uint256 stuckValidatorsCount,
/* uint256 refundedValidatorsCount */,
/* uint256 stuckPenaltyEndTimestamp */,
uint256 totalExitedValidators,
/* uint256 totalDepositedValidators */,
/* uint256 depositableValidatorsCount */
) = IStakingModule(moduleAddr).getNodeOperatorSummary(_nodeOperatorId);
if (_correction.currentModuleExitedValidatorsCount != stakingModule.exitedValidatorsCount ||
_correction.currentNodeOperatorExitedValidatorsCount != totalExitedValidators ||
_correction.currentNodeOperatorStuckValidatorsCount != stuckValidatorsCount
) {
revert UnexpectedCurrentValidatorsCount(
stakingModule.exitedValidatorsCount,
totalExitedValidators,
stuckValidatorsCount
);
}
stakingModule.exitedValidatorsCount = _correction.newModuleExitedValidatorsCount;
IStakingModule(moduleAddr).unsafeUpdateValidatorsCount(
_nodeOperatorId,
_correction.newNodeOperatorExitedValidatorsCount,
_correction.newNodeOperatorStuckValidatorsCount
);
if (_triggerUpdateFinish) {
IStakingModule(moduleAddr).onExitedAndStuckValidatorsCountsUpdated();
}
}
/// @notice Updates stuck validators counts per node operator for the staking module with
/// the specified id.
///
/// See the docs for `updateExitedValidatorsCountByStakingModule` for the description of the
/// overall update process.
///
/// @param _stakingModuleId The id of the staking modules to be updated.
/// @param _nodeOperatorIds Ids of the node operators to be updated.
/// @param _stuckValidatorsCounts New counts of stuck validators for the specified node operators.
///
function reportStakingModuleStuckValidatorsCountByNodeOperator(
uint256 _stakingModuleId,
bytes calldata _nodeOperatorIds,
bytes calldata _stuckValidatorsCounts
)
external
onlyRole(REPORT_EXITED_VALIDATORS_ROLE)
{
address moduleAddr = _getStakingModuleById(_stakingModuleId).stakingModuleAddress;
_checkValidatorsByNodeOperatorReportData(_nodeOperatorIds, _stuckValidatorsCounts);
IStakingModule(moduleAddr).updateStuckValidatorsCount(_nodeOperatorIds, _stuckValidatorsCounts);
}
/// @notice Called by the oracle when the second phase of data reporting finishes, i.e. when the
/// oracle submitted the complete data on the stuck and exited validator counts per node operator
/// for the current reporting frame.
///
/// See the docs for `updateExitedValidatorsCountByStakingModule` for the description of the
/// overall update process.
///
function onValidatorsCountsByNodeOperatorReportingFinished()
external
onlyRole(REPORT_EXITED_VALIDATORS_ROLE)
{
uint256 stakingModulesCount = getStakingModulesCount();
for (uint256 i; i < stakingModulesCount; ) {
StakingModule storage stakingModule = _getStakingModuleByIndex(i);
IStakingModule moduleContract = IStakingModule(stakingModule.stakingModuleAddress);
(uint256 exitedValidatorsCount, , ) = moduleContract.getStakingModuleSummary();
if (exitedValidatorsCount == stakingModule.exitedValidatorsCount) {
// oracle finished updating exited validators for all node ops
try moduleContract.onExitedAndStuckValidatorsCountsUpdated() {}
catch (bytes memory lowLevelRevertData) {
/// @dev This check is required to prevent incorrect gas estimation of the method.
/// Without it, Ethereum nodes that use binary search for gas estimation may
/// return an invalid value when the onExitedAndStuckValidatorsCountsUpdated()
/// reverts because of the "out of gas" error. Here we assume that the
/// onExitedAndStuckValidatorsCountsUpdated() method doesn't have reverts with
/// empty error data except "out of gas".
if (lowLevelRevertData.length == 0) revert UnrecoverableModuleError();
emit ExitedAndStuckValidatorsCountsUpdateFailed(
stakingModule.id,
lowLevelRevertData
);
}
}
unchecked { ++i; }
}
}
/**
* @notice Returns all registered staking modules
*/
function getStakingModules() external view returns (StakingModule[] memory res) {
uint256 stakingModulesCount = getStakingModulesCount();
res = new StakingModule[](stakingModulesCount);
for (uint256 i; i < stakingModulesCount; ) {
res[i] = _getStakingModuleByIndex(i);
unchecked {
++i;
}
}
}
/**
* @notice Returns the ids of all registered staking modules
*/
function getStakingModuleIds() public view returns (uint256[] memory stakingModuleIds) {
uint256 stakingModulesCount = getStakingModulesCount();
stakingModuleIds = new uint256[](stakingModulesCount);
for (uint256 i; i < stakingModulesCount; ) {
stakingModuleIds[i] = _getStakingModuleByIndex(i).id;
unchecked {
++i;
}
}
}
/**
* @dev Returns staking module by id
*/
function getStakingModule(uint256 _stakingModuleId)
public
view
returns (StakingModule memory)
{
return _getStakingModuleById(_stakingModuleId);
}
/**
* @dev Returns total number of staking modules
*/
function getStakingModulesCount() public view returns (uint256) {
return STAKING_MODULES_COUNT_POSITION.getStorageUint256();
}
/**
* @dev Returns true if staking module with the given id was registered via `addStakingModule`, false otherwise
*/
function hasStakingModule(uint256 _stakingModuleId) external view returns (bool) {
return _getStorageStakingIndicesMapping()[_stakingModuleId] != 0;
}
/**
* @dev Returns status of staking module
*/
function getStakingModuleStatus(uint256 _stakingModuleId)
public
view
returns (StakingModuleStatus)
{
return StakingModuleStatus(_getStakingModuleById(_stakingModuleId).status);
}
/// @notice A summary of the staking module's validators
struct StakingModuleSummary {
/// @notice The total number of validators in the EXITED state on the Consensus Layer
/// @dev This value can't decrease in normal conditions
uint256 totalExitedValidators;
/// @notice The total number of validators deposited via the official Deposit Contract
/// @dev This value is a cumulative counter: even when the validator goes into EXITED state this
/// counter is not decreasing
uint256 totalDepositedValidators;
/// @notice The number of validators in the set available for deposit
uint256 depositableValidatorsCount;
}
/// @notice A summary of node operator and its validators
struct NodeOperatorSummary {
/// @notice Shows whether the current target limit applied to the node operator
bool isTargetLimitActive;
/// @notice Relative target active validators limit for operator
uint256 targetValidatorsCount;
/// @notice The number of validators with an expired request to exit time
uint256 stuckValidatorsCount;
/// @notice The number of validators that can't be withdrawn, but deposit costs were
/// compensated to the Lido by the node operator
uint256 refundedValidatorsCount;
/// @notice A time when the penalty for stuck validators stops applying to node operator rewards
uint256 stuckPenaltyEndTimestamp;
/// @notice The total number of validators in the EXITED state on the Consensus Layer
/// @dev This value can't decrease in normal conditions
uint256 totalExitedValidators;
/// @notice The total number of validators deposited via the official Deposit Contract
/// @dev This value is a cumulative counter: even when the validator goes into EXITED state this
/// counter is not decreasing
uint256 totalDepositedValidators;
/// @notice The number of validators in the set available for deposit
uint256 depositableValidatorsCount;
}
/// @notice Returns all-validators summary in the staking module
/// @param _stakingModuleId id of the staking module to return summary for
function getStakingModuleSummary(uint256 _stakingModuleId)
public
view
returns (StakingModuleSummary memory summary)
{
StakingModule memory stakingModuleState = getStakingModule(_stakingModuleId);
IStakingModule stakingModule = IStakingModule(stakingModuleState.stakingModuleAddress);
(
summary.totalExitedValidators,
summary.totalDepositedValidators,
summary.depositableValidatorsCount
) = stakingModule.getStakingModuleSummary();
}
/// @notice Returns node operator summary from the staking module
/// @param _stakingModuleId id of the staking module where node operator is onboarded
/// @param _nodeOperatorId id of the node operator to return summary for
function getNodeOperatorSummary(uint256 _stakingModuleId, uint256 _nodeOperatorId)
public
view
returns (NodeOperatorSummary memory summary)
{
StakingModule memory stakingModuleState = getStakingModule(_stakingModuleId);
IStakingModule stakingModule = IStakingModule(stakingModuleState.stakingModuleAddress);
/// @dev using intermediate variables below due to "Stack too deep" error in case of
/// assigning directly into the NodeOperatorSummary struct
(
bool isTargetLimitActive,
uint256 targetValidatorsCount,
uint256 stuckValidatorsCount,
uint256 refundedValidatorsCount,
uint256 stuckPenaltyEndTimestamp,
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
) = stakingModule.getNodeOperatorSummary(_nodeOperatorId);
summary.isTargetLimitActive = isTargetLimitActive;
summary.targetValidatorsCount = targetValidatorsCount;
summary.stuckValidatorsCount = stuckValidatorsCount;
summary.refundedValidatorsCount = refundedValidatorsCount;
summary.stuckPenaltyEndTimestamp = stuckPenaltyEndTimestamp;
summary.totalExitedValidators = totalExitedValidators;
summary.totalDepositedValidators = totalDepositedValidators;
summary.depositableValidatorsCount = depositableValidatorsCount;
}
/// @notice A collection of the staking module data stored across the StakingRouter and the
/// staking module contract
/// @dev This data, first of all, is designed for off-chain usage and might be redundant for
/// on-chain calls. Give preference for dedicated methods for gas-efficient on-chain calls
struct StakingModuleDigest {
/// @notice The number of node operators registered in the staking module
uint256 nodeOperatorsCount;
/// @notice The number of node operators registered in the staking module in active state
uint256 activeNodeOperatorsCount;
/// @notice The current state of the staking module taken from the StakingRouter
StakingModule state;
/// @notice A summary of the staking module's validators
StakingModuleSummary summary;
}
/// @notice A collection of the node operator data stored in the staking module
/// @dev This data, first of all, is designed for off-chain usage and might be redundant for
/// on-chain calls. Give preference for dedicated methods for gas-efficient on-chain calls
struct NodeOperatorDigest {
/// @notice id of the node operator
uint256 id;
/// @notice Shows whether the node operator is active or not
bool isActive;
/// @notice A summary of node operator and its validators
NodeOperatorSummary summary;
}
/// @notice Returns staking module digest for each staking module registered in the staking router
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation
function getAllStakingModuleDigests() external view returns (StakingModuleDigest[] memory) {
return getStakingModuleDigests(getStakingModuleIds());
}
/// @notice Returns staking module digest for passed staking module ids
/// @param _stakingModuleIds ids of the staking modules to return data for
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation
function getStakingModuleDigests(uint256[] memory _stakingModuleIds)
public
view
returns (StakingModuleDigest[] memory digests)
{
digests = new StakingModuleDigest[](_stakingModuleIds.length);
for (uint256 i = 0; i < _stakingModuleIds.length; ++i) {
StakingModule memory stakingModuleState = getStakingModule(_stakingModuleIds[i]);
IStakingModule stakingModule = IStakingModule(stakingModuleState.stakingModuleAddress);
digests[i] = StakingModuleDigest({
nodeOperatorsCount: stakingModule.getNodeOperatorsCount(),
activeNodeOperatorsCount: stakingModule.getActiveNodeOperatorsCount(),
state: stakingModuleState,
summary: getStakingModuleSummary(_stakingModuleIds[i])
});
}
}
/// @notice Returns node operator digest for each node operator registered in the given staking module
/// @param _stakingModuleId id of the staking module to return data for
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation
function getAllNodeOperatorDigests(uint256 _stakingModuleId) external view returns (NodeOperatorDigest[] memory) {
IStakingModule stakingModule = IStakingModule(_getStakingModuleAddressById(_stakingModuleId));
uint256 nodeOperatorsCount = stakingModule.getNodeOperatorsCount();
return getNodeOperatorDigests(_stakingModuleId, 0, nodeOperatorsCount);
}
/// @notice Returns node operator digest for passed node operator ids in the given staking module
/// @param _stakingModuleId id of the staking module where node operators registered
/// @param _offset node operators offset starting with 0
/// @param _limit the max number of node operators to return
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation
function getNodeOperatorDigests(
uint256 _stakingModuleId,
uint256 _offset,
uint256 _limit
) public view returns (NodeOperatorDigest[] memory) {
IStakingModule stakingModule = IStakingModule(_getStakingModuleAddressById(_stakingModuleId));
uint256[] memory nodeOperatorIds = stakingModule.getNodeOperatorIds(_offset, _limit);
return getNodeOperatorDigests(_stakingModuleId, nodeOperatorIds);
}
/// @notice Returns node operator digest for a slice of node operators registered in the given
/// staking module
/// @param _stakingModuleId id of the staking module where node operators registered
/// @param _nodeOperatorIds ids of the node operators to return data for
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation
function getNodeOperatorDigests(uint256 _stakingModuleId, uint256[] memory _nodeOperatorIds)
public
view
returns (NodeOperatorDigest[] memory digests)
{
IStakingModule stakingModule = IStakingModule(_getStakingModuleAddressById(_stakingModuleId));
digests = new NodeOperatorDigest[](_nodeOperatorIds.length);
for (uint256 i = 0; i < _nodeOperatorIds.length; ++i) {
digests[i] = NodeOperatorDigest({
id: _nodeOperatorIds[i],
isActive: stakingModule.getNodeOperatorIsActive(_nodeOperatorIds[i]),
summary: getNodeOperatorSummary(_stakingModuleId, _nodeOperatorIds[i])
});
}
}
/**
* @notice set the staking module status flag for participation in further deposits and/or reward distribution
*/
function setStakingModuleStatus(uint256 _stakingModuleId, StakingModuleStatus _status) external
onlyRole(STAKING_MODULE_MANAGE_ROLE)
{
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
if (StakingModuleStatus(stakingModule.status) == _status)
revert StakingModuleStatusTheSame();
_setStakingModuleStatus(stakingModule, _status);
}
/**
* @notice pause deposits for staking module
* @param _stakingModuleId id of the staking module to be paused
*/
function pauseStakingModule(uint256 _stakingModuleId) external
onlyRole(STAKING_MODULE_PAUSE_ROLE)
{
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
if (StakingModuleStatus(stakingModule.status) != StakingModuleStatus.Active)
revert StakingModuleNotActive();
_setStakingModuleStatus(stakingModule, StakingModuleStatus.DepositsPaused);
}
/**
* @notice resume deposits for staking module
* @param _stakingModuleId id of the staking module to be unpaused
*/
function resumeStakingModule(uint256 _stakingModuleId) external
onlyRole(STAKING_MODULE_RESUME_ROLE)
{
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
if (StakingModuleStatus(stakingModule.status) != StakingModuleStatus.DepositsPaused)
revert StakingModuleNotPaused();
_setStakingModuleStatus(stakingModule, StakingModuleStatus.Active);
}
function getStakingModuleIsStopped(uint256 _stakingModuleId) external view returns (bool)
{
return getStakingModuleStatus(_stakingModuleId) == StakingModuleStatus.Stopped;
}
function getStakingModuleIsDepositsPaused(uint256 _stakingModuleId)
external
view
returns (bool)
{
return getStakingModuleStatus(_stakingModuleId) == StakingModuleStatus.DepositsPaused;
}
function getStakingModuleIsActive(uint256 _stakingModuleId) external view returns (bool) {
return getStakingModuleStatus(_stakingModuleId) == StakingModuleStatus.Active;
}
function getStakingModuleNonce(uint256 _stakingModuleId) external view returns (uint256) {
return IStakingModule(_getStakingModuleAddressById(_stakingModuleId)).getNonce();
}
function getStakingModuleLastDepositBlock(uint256 _stakingModuleId)
external
view
returns (uint256)
{
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
return stakingModule.lastDepositBlock;
}
function getStakingModuleActiveValidatorsCount(uint256 _stakingModuleId)
external
view
returns (uint256 activeValidatorsCount)
{
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
(
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
/* uint256 depositableValidatorsCount */
) = IStakingModule(stakingModule.stakingModuleAddress).getStakingModuleSummary();
activeValidatorsCount = totalDepositedValidators - Math256.max(
stakingModule.exitedValidatorsCount, totalExitedValidators
);
}
/// @dev calculate the max count of deposits which the staking module can provide data for based
/// on the passed `_maxDepositsValue` amount
/// @param _stakingModuleId id of the staking module to be deposited
/// @param _maxDepositsValue max amount of ether that might be used for deposits count calculation
/// @return max number of deposits might be done using the given staking module
function getStakingModuleMaxDepositsCount(uint256 _stakingModuleId, uint256 _maxDepositsValue)
public
view
returns (uint256)
{
(
/* uint256 allocated */,
uint256[] memory newDepositsAllocation,
StakingModuleCache[] memory stakingModulesCache
) = _getDepositsAllocation(_maxDepositsValue / DEPOSIT_SIZE);
uint256 stakingModuleIndex = _getStakingModuleIndexById(_stakingModuleId);
return
newDepositsAllocation[stakingModuleIndex] - stakingModulesCache[stakingModuleIndex].activeValidatorsCount;
}
/**
* @notice Returns the aggregate fee distribution proportion
* @return modulesFee modules aggregate fee in base precision
* @return treasuryFee treasury fee in base precision
* @return basePrecision base precision: a value corresponding to the full fee
*/
function getStakingFeeAggregateDistribution() public view returns (
uint96 modulesFee,
uint96 treasuryFee,
uint256 basePrecision
) {
uint96[] memory moduleFees;
uint96 totalFee;
(, , moduleFees, totalFee, basePrecision) = getStakingRewardsDistribution();
for (uint256 i; i < moduleFees.length; ++i) {
modulesFee += moduleFees[i];
}
treasuryFee = totalFee - modulesFee;
}
/**
* @notice Return shares table
*
* @return recipients rewards recipient addresses corresponding to each module
* @return stakingModuleIds module IDs
* @return stakingModuleFees fee of each recipient
* @return totalFee total fee to mint for each staking module and treasury
* @return precisionPoints base precision number, which constitutes 100% fee
*/
function getStakingRewardsDistribution()
public
view
returns (
address[] memory recipients,
uint256[] memory stakingModuleIds,
uint96[] memory stakingModuleFees,
uint96 totalFee,
uint256 precisionPoints
)
{
(uint256 totalActiveValidators, StakingModuleCache[] memory stakingModulesCache) = _loadStakingModulesCache();
uint256 stakingModulesCount = stakingModulesCache.length;
/// @dev return empty response if there are no staking modules or active validators yet
if (stakingModulesCount == 0 || totalActiveValidators == 0) {
return (new address[](0), new uint256[](0), new uint96[](0), 0, FEE_PRECISION_POINTS);
}
precisionPoints = FEE_PRECISION_POINTS;
stakingModuleIds = new uint256[](stakingModulesCount);
recipients = new address[](stakingModulesCount);
stakingModuleFees = new uint96[](stakingModulesCount);
uint256 rewardedStakingModulesCount = 0;
uint256 stakingModuleValidatorsShare;
uint96 stakingModuleFee;
for (uint256 i; i < stakingModulesCount; ) {
/// @dev skip staking modules which have no active validators
if (stakingModulesCache[i].activeValidatorsCount > 0) {
stakingModuleIds[rewardedStakingModulesCount] = stakingModulesCache[i].stakingModuleId;
stakingModuleValidatorsShare = ((stakingModulesCache[i].activeValidatorsCount * precisionPoints) / totalActiveValidators);
recipients[rewardedStakingModulesCount] = address(stakingModulesCache[i].stakingModuleAddress);
stakingModuleFee = uint96((stakingModuleValidatorsShare * stakingModulesCache[i].stakingModuleFee) / TOTAL_BASIS_POINTS);
/// @dev if the staking module has the `Stopped` status for some reason, then
/// the staking module's rewards go to the treasury, so that the DAO has ability
/// to manage them (e.g. to compensate the staking module in case of an error, etc.)
if (stakingModulesCache[i].status != StakingModuleStatus.Stopped) {
stakingModuleFees[rewardedStakingModulesCount] = stakingModuleFee;
}
// else keep stakingModuleFees[rewardedStakingModulesCount] = 0, but increase totalFee
totalFee += (uint96((stakingModuleValidatorsShare * stakingModulesCache[i].treasuryFee) / TOTAL_BASIS_POINTS) + stakingModuleFee);
unchecked {
rewardedStakingModulesCount++;
}
}
unchecked {
++i;
}
}
// Total fee never exceeds 100%
assert(totalFee <= precisionPoints);
/// @dev shrink arrays
if (rewardedStakingModulesCount < stakingModulesCount) {
assembly {
mstore(stakingModuleIds, rewardedStakingModulesCount)
mstore(recipients, rewardedStakingModulesCount)
mstore(stakingModuleFees, rewardedStakingModulesCount)
}
}
}
/// @notice Helper for Lido contract (DEPRECATED)
/// Returns total fee total fee to mint for each staking
/// module and treasury in reduced, 1e4 precision.
/// In integrations please use getStakingRewardsDistribution().
/// reduced, 1e4 precision.
function getTotalFeeE4Precision() external view returns (uint16 totalFee) {
/// @dev The logic is placed here but in Lido contract to save Lido bytecode
(, , , uint96 totalFeeInHighPrecision, uint256 precision) = getStakingRewardsDistribution();
// Here we rely on (totalFeeInHighPrecision <= precision)
totalFee = _toE4Precision(totalFeeInHighPrecision, precision);
}
/// @notice Helper for Lido contract (DEPRECATED)
/// Returns the same as getStakingFeeAggregateDistribution() but in reduced, 1e4 precision
/// @dev Helper only for Lido contract. Use getStakingFeeAggregateDistribution() instead
function getStakingFeeAggregateDistributionE4Precision()
external view
returns (uint16 modulesFee, uint16 treasuryFee)
{
/// @dev The logic is placed here but in Lido contract to save Lido bytecode
(
uint256 modulesFeeHighPrecision,
uint256 treasuryFeeHighPrecision,
uint256 precision
) = getStakingFeeAggregateDistribution();
// Here we rely on ({modules,treasury}FeeHighPrecision <= precision)
modulesFee = _toE4Precision(modulesFeeHighPrecision, precision);
treasuryFee = _toE4Precision(treasuryFeeHighPrecision, precision);
}
/// @notice returns new deposits allocation after the distribution of the `_depositsCount` deposits
function getDepositsAllocation(uint256 _depositsCount) external view returns (uint256 allocated, uint256[] memory allocations) {
(allocated, allocations, ) = _getDepositsAllocation(_depositsCount);
}
/// @dev Invokes a deposit call to the official Deposit contract
/// @param _depositsCount number of deposits to make
/// @param _stakingModuleId id of the staking module to be deposited
/// @param _depositCalldata staking module calldata
function deposit(
uint256 _depositsCount,
uint256 _stakingModuleId,
bytes calldata _depositCalldata
) external payable {
if (msg.sender != LIDO_POSITION.getStorageAddress()) revert AppAuthLidoFailed();
bytes32 withdrawalCredentials = getWithdrawalCredentials();
if (withdrawalCredentials == 0) revert EmptyWithdrawalsCredentials();
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
if (StakingModuleStatus(stakingModule.status) != StakingModuleStatus.Active)
revert StakingModuleNotActive();
/// @dev firstly update the local state of the contract to prevent a reentrancy attack
/// even though the staking modules are trusted contracts
stakingModule.lastDepositAt = uint64(block.timestamp);
stakingModule.lastDepositBlock = block.number;
uint256 depositsValue = msg.value;
emit StakingRouterETHDeposited(_stakingModuleId, depositsValue);
if (depositsValue != _depositsCount * DEPOSIT_SIZE)
revert InvalidDepositsValue(depositsValue, _depositsCount);
if (_depositsCount > 0) {
(bytes memory publicKeysBatch, bytes memory signaturesBatch) =
IStakingModule(stakingModule.stakingModuleAddress)
.obtainDepositData(_depositsCount, _depositCalldata);
uint256 etherBalanceBeforeDeposits = address(this).balance;
_makeBeaconChainDeposits32ETH(
_depositsCount,
abi.encodePacked(withdrawalCredentials),
publicKeysBatch,
signaturesBatch
);
uint256 etherBalanceAfterDeposits = address(this).balance;
/// @dev all sent ETH must be deposited and self balance stay the same
assert(etherBalanceBeforeDeposits - etherBalanceAfterDeposits == depositsValue);
}
}
/**
* @notice Set credentials to withdraw ETH on Consensus Layer side after the phase 2 is launched to `_withdrawalCredentials`
* @dev Note that setWithdrawalCredentials discards all unused deposits data as the signatures are invalidated.
* @param _withdrawalCredentials withdrawal credentials field as defined in the Ethereum PoS consensus specs
*/
function setWithdrawalCredentials(bytes32 _withdrawalCredentials) external onlyRole(MANAGE_WITHDRAWAL_CREDENTIALS_ROLE) {
WITHDRAWAL_CREDENTIALS_POSITION.setStorageBytes32(_withdrawalCredentials);
uint256 stakingModulesCount = getStakingModulesCount();
for (uint256 i; i < stakingModulesCount; ) {
StakingModule storage stakingModule = _getStakingModuleByIndex(i);
unchecked { ++i; }
try IStakingModule(stakingModule.stakingModuleAddress)
.onWithdrawalCredentialsChanged() {}
catch (bytes memory lowLevelRevertData) {
/// @dev This check is required to prevent incorrect gas estimation of the method.
/// Without it, Ethereum nodes that use binary search for gas estimation may
/// return an invalid value when the onWithdrawalCredentialsChanged()
/// reverts because of the "out of gas" error. Here we assume that the
/// onWithdrawalCredentialsChanged() method doesn't have reverts with
/// empty error data except "out of gas".
if (lowLevelRevertData.length == 0) revert UnrecoverableModuleError();
_setStakingModuleStatus(stakingModule, StakingModuleStatus.DepositsPaused);
emit WithdrawalsCredentialsChangeFailed(stakingModule.id, lowLevelRevertData);
}
}
emit WithdrawalCredentialsSet(_withdrawalCredentials, msg.sender);
}
/**
* @notice Returns current credentials to withdraw ETH on Consensus Layer side after the phase 2 is launched
*/
function getWithdrawalCredentials() public view returns (bytes32) {
return WITHDRAWAL_CREDENTIALS_POSITION.getStorageBytes32();
}
function _checkValidatorsByNodeOperatorReportData(
bytes calldata _nodeOperatorIds,
bytes calldata _validatorsCounts
) internal pure {
if (_nodeOperatorIds.length % 8 != 0 || _validatorsCounts.length % 16 != 0) {
revert InvalidReportData(3);
}
uint256 nodeOperatorsCount = _nodeOperatorIds.length / 8;
if (_validatorsCounts.length / 16 != nodeOperatorsCount) {
revert InvalidReportData(2);
}
if (nodeOperatorsCount == 0) {
revert InvalidReportData(1);
}
}
/// @dev load modules into a memory cache
///
/// @return totalActiveValidators total active validators across all modules
/// @return stakingModulesCache array of StakingModuleCache structs
function _loadStakingModulesCache() internal view returns (
uint256 totalActiveValidators,
StakingModuleCache[] memory stakingModulesCache
) {
uint256 stakingModulesCount = getStakingModulesCount();
stakingModulesCache = new StakingModuleCache[](stakingModulesCount);
for (uint256 i; i < stakingModulesCount; ) {
stakingModulesCache[i] = _loadStakingModulesCacheItem(i);
totalActiveValidators += stakingModulesCache[i].activeValidatorsCount;
unchecked {
++i;
}
}
}
function _loadStakingModulesCacheItem(uint256 _stakingModuleIndex)
internal
view
returns (StakingModuleCache memory cacheItem)
{
StakingModule storage stakingModuleData = _getStakingModuleByIndex(_stakingModuleIndex);
cacheItem.stakingModuleAddress = stakingModuleData.stakingModuleAddress;
cacheItem.stakingModuleId = stakingModuleData.id;
cacheItem.stakingModuleFee = stakingModuleData.stakingModuleFee;
cacheItem.treasuryFee = stakingModuleData.treasuryFee;
cacheItem.targetShare = stakingModuleData.targetShare;
cacheItem.status = StakingModuleStatus(stakingModuleData.status);
(
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
) = IStakingModule(cacheItem.stakingModuleAddress).getStakingModuleSummary();
cacheItem.availableValidatorsCount = cacheItem.status == StakingModuleStatus.Active
? depositableValidatorsCount
: 0;
// the module might not receive all exited validators data yet => we need to replacing
// the exitedValidatorsCount with the one that the staking router is aware of
cacheItem.activeValidatorsCount =
totalDepositedValidators -
Math256.max(totalExitedValidators, stakingModuleData.exitedValidatorsCount);
}
function _setStakingModuleStatus(StakingModule storage _stakingModule, StakingModuleStatus _status) internal {
StakingModuleStatus prevStatus = StakingModuleStatus(_stakingModule.status);
if (prevStatus != _status) {
_stakingModule.status = uint8(_status);
emit StakingModuleStatusSet(_stakingModule.id, _status, msg.sender);
}
}
function _getDepositsAllocation(
uint256 _depositsToAllocate
) internal view returns (uint256 allocated, uint256[] memory allocations, StakingModuleCache[] memory stakingModulesCache) {
// calculate total used validators for operators
uint256 totalActiveValidators;
(totalActiveValidators, stakingModulesCache) = _loadStakingModulesCache();
uint256 stakingModulesCount = stakingModulesCache.length;
allocations = new uint256[](stakingModulesCount);
if (stakingModulesCount > 0) {
/// @dev new estimated active validators count
totalActiveValidators += _depositsToAllocate;
uint256[] memory capacities = new uint256[](stakingModulesCount);
uint256 targetValidators;
for (uint256 i; i < stakingModulesCount; ) {
allocations[i] = stakingModulesCache[i].activeValidatorsCount;
targetValidators = (stakingModulesCache[i].targetShare * totalActiveValidators) / TOTAL_BASIS_POINTS;
capacities[i] = Math256.min(targetValidators, stakingModulesCache[i].activeValidatorsCount + stakingModulesCache[i].availableValidatorsCount);
unchecked {
++i;
}
}
allocated = MinFirstAllocationStrategy.allocate(allocations, capacities, _depositsToAllocate);
}
}
function _getStakingModuleIndexById(uint256 _stakingModuleId) internal view returns (uint256) {
mapping(uint256 => uint256) storage _stakingModuleIndicesOneBased = _getStorageStakingIndicesMapping();
uint256 indexOneBased = _stakingModuleIndicesOneBased[_stakingModuleId];
if (indexOneBased == 0) revert StakingModuleUnregistered();
return indexOneBased - 1;
}
function _setStakingModuleIndexById(uint256 _stakingModuleId, uint256 _stakingModuleIndex) internal {
mapping(uint256 => uint256) storage _stakingModuleIndicesOneBased = _getStorageStakingIndicesMapping();
_stakingModuleIndicesOneBased[_stakingModuleId] = _stakingModuleIndex + 1;
}
function _getStakingModuleById(uint256 _stakingModuleId) internal view returns (StakingModule storage) {
return _getStakingModuleByIndex(_getStakingModuleIndexById(_stakingModuleId));
}
function _getStakingModuleByIndex(uint256 _stakingModuleIndex) internal view returns (StakingModule storage) {
mapping(uint256 => StakingModule) storage _stakingModules = _getStorageStakingModulesMapping();
return _stakingModules[_stakingModuleIndex];
}
function _getStakingModuleAddressById(uint256 _stakingModuleId) internal view returns (address) {
return _getStakingModuleById(_stakingModuleId).stakingModuleAddress;
}
function _getStorageStakingModulesMapping() internal pure returns (mapping(uint256 => StakingModule) storage result) {
bytes32 position = STAKING_MODULES_MAPPING_POSITION;
assembly {
result.slot := position
}
}
function _getStorageStakingIndicesMapping() internal pure returns (mapping(uint256 => uint256) storage result) {
bytes32 position = STAKING_MODULE_INDICES_MAPPING_POSITION;
assembly {
result.slot := position
}
}
function _toE4Precision(uint256 _value, uint256 _precision) internal pure returns (uint16) {
return uint16((_value * TOTAL_BASIS_POINTS) / _precision);
}
}
@openzeppelin/contracts-v4.4/access/IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
@openzeppelin/contracts-v4.4/access/IAccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
@openzeppelin/contracts-v4.4/utils/Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
@openzeppelin/contracts-v4.4/utils/Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
@openzeppelin/contracts-v4.4/utils/introspection/ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
@openzeppelin/contracts-v4.4/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
@openzeppelin/contracts-v4.4/utils/structs/EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* 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.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
// 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;
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 on 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;
assembly {
result := store
}
return result;
}
}
contracts/0.8.9/BeaconChainDepositor.sol
// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
// See contracts/COMPILERS.md
pragma solidity 0.8.9;
import {MemUtils} from "../common/lib/MemUtils.sol";
interface IDepositContract {
function get_deposit_root() external view returns (bytes32 rootHash);
function deposit(
bytes calldata pubkey, // 48 bytes
bytes calldata withdrawal_credentials, // 32 bytes
bytes calldata signature, // 96 bytes
bytes32 deposit_data_root
) external payable;
}
contract BeaconChainDepositor {
uint256 internal constant PUBLIC_KEY_LENGTH = 48;
uint256 internal constant SIGNATURE_LENGTH = 96;
uint256 internal constant DEPOSIT_SIZE = 32000000 ether;
/// @dev deposit amount 32 mln eth in gweis converted to little endian uint64
/// DEPOSIT_SIZE_IN_GWEI_LE64 = toLittleEndian64(32 mln ether / 1 gwei)
uint64 internal constant DEPOSIT_SIZE_IN_GWEI_LE64 = 0x0000D098D4AF7100;
IDepositContract public immutable DEPOSIT_CONTRACT;
constructor(address _depositContract) {
if (_depositContract == address(0)) revert DepositContractZeroAddress();
DEPOSIT_CONTRACT = IDepositContract(_depositContract);
}
/// @dev Invokes a deposit call to the official Beacon Deposit contract
/// @param _keysCount amount of keys to deposit
/// @param _withdrawalCredentials Commitment to a public key for withdrawals
/// @param _publicKeysBatch A BLS12-381 public keys batch
/// @param _signaturesBatch A BLS12-381 signatures batch
function _makeBeaconChainDeposits32ETH(
uint256 _keysCount,
bytes memory _withdrawalCredentials,
bytes memory _publicKeysBatch,
bytes memory _signaturesBatch
) internal {
if (_publicKeysBatch.length != PUBLIC_KEY_LENGTH * _keysCount) {
revert InvalidPublicKeysBatchLength(_publicKeysBatch.length, PUBLIC_KEY_LENGTH * _keysCount);
}
if (_signaturesBatch.length != SIGNATURE_LENGTH * _keysCount) {
revert InvalidSignaturesBatchLength(_signaturesBatch.length, SIGNATURE_LENGTH * _keysCount);
}
bytes memory publicKey = MemUtils.unsafeAllocateBytes(PUBLIC_KEY_LENGTH);
bytes memory signature = MemUtils.unsafeAllocateBytes(SIGNATURE_LENGTH);
for (uint256 i; i < _keysCount;) {
MemUtils.copyBytes(_publicKeysBatch, publicKey, i * PUBLIC_KEY_LENGTH, 0, PUBLIC_KEY_LENGTH);
MemUtils.copyBytes(_signaturesBatch, signature, i * SIGNATURE_LENGTH, 0, SIGNATURE_LENGTH);
DEPOSIT_CONTRACT.deposit{value: DEPOSIT_SIZE}(
publicKey, _withdrawalCredentials, signature, _computeDepositDataRoot(_withdrawalCredentials, publicKey, signature)
);
unchecked {
++i;
}
}
}
/// @dev computes the deposit_root_hash required by official Beacon Deposit contract
/// @param _publicKey A BLS12-381 public key.
/// @param _signature A BLS12-381 signature
function _computeDepositDataRoot(bytes memory _withdrawalCredentials, bytes memory _publicKey, bytes memory _signature)
private
pure
returns (bytes32)
{
// Compute deposit data root (`DepositData` hash tree root) according to deposit_contract.sol
bytes memory sigPart1 = MemUtils.unsafeAllocateBytes(64);
bytes memory sigPart2 = MemUtils.unsafeAllocateBytes(SIGNATURE_LENGTH - 64);
MemUtils.copyBytes(_signature, sigPart1, 0, 0, 64);
MemUtils.copyBytes(_signature, sigPart2, 64, 0, SIGNATURE_LENGTH - 64);
bytes32 publicKeyRoot = sha256(abi.encodePacked(_publicKey, bytes16(0)));
bytes32 signatureRoot = sha256(abi.encodePacked(sha256(abi.encodePacked(sigPart1)), sha256(abi.encodePacked(sigPart2, bytes32(0)))));
return sha256(
abi.encodePacked(
sha256(abi.encodePacked(publicKeyRoot, _withdrawalCredentials)),
sha256(abi.encodePacked(DEPOSIT_SIZE_IN_GWEI_LE64, bytes24(0), signatureRoot))
)
);
}
error DepositContractZeroAddress();
error InvalidPublicKeysBatchLength(uint256 actual, uint256 expected);
error InvalidSignaturesBatchLength(uint256 actual, uint256 expected);
}
contracts/0.8.9/interfaces/IStakingModule.sol
// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.9;
/// @title Lido's Staking Module interface
interface IStakingModule {
/// @notice Returns the type of the staking module
function getType() external view returns (bytes32);
/// @notice Returns all-validators summary in the staking module
/// @return totalExitedValidators total number of validators in the EXITED state
/// on the Consensus Layer. This value can't decrease in normal conditions
/// @return totalDepositedValidators total number of validators deposited via the
/// official Deposit Contract. This value is a cumulative counter: even when the validator
/// goes into EXITED state this counter is not decreasing
/// @return depositableValidatorsCount number of validators in the set available for deposit
function getStakingModuleSummary() external view returns (
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
);
/// @notice Returns all-validators summary belonging to the node operator with the given id
/// @param _nodeOperatorId id of the operator to return report for
/// @return isTargetLimitActive shows whether the current target limit applied to the node operator
/// @return targetValidatorsCount relative target active validators limit for operator
/// @return stuckValidatorsCount number of validators with an expired request to exit time
/// @return refundedValidatorsCount number of validators that can't be withdrawn, but deposit
/// costs were compensated to the Lido by the node operator
/// @return stuckPenaltyEndTimestamp time when the penalty for stuck validators stops applying
/// to node operator rewards
/// @return totalExitedValidators total number of validators in the EXITED state
/// on the Consensus Layer. This value can't decrease in normal conditions
/// @return totalDepositedValidators total number of validators deposited via the official
/// Deposit Contract. This value is a cumulative counter: even when the validator goes into
/// EXITED state this counter is not decreasing
/// @return depositableValidatorsCount number of validators in the set available for deposit
function getNodeOperatorSummary(uint256 _nodeOperatorId) external view returns (
bool isTargetLimitActive,
uint256 targetValidatorsCount,
uint256 stuckValidatorsCount,
uint256 refundedValidatorsCount,
uint256 stuckPenaltyEndTimestamp,
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
);
/// @notice Returns a counter that MUST change its value whenever the deposit data set changes.
/// Below is the typical list of actions that requires an update of the nonce:
/// 1. a node operator's deposit data is added
/// 2. a node operator's deposit data is removed
/// 3. a node operator's ready-to-deposit data size is changed
/// 4. a node operator was activated/deactivated
/// 5. a node operator's deposit data is used for the deposit
/// Note: Depending on the StakingModule implementation above list might be extended
/// @dev In some scenarios, it's allowed to update nonce without actual change of the deposit
/// data subset, but it MUST NOT lead to the DOS of the staking module via continuous
/// update of the nonce by the malicious actor
function getNonce() external view returns (uint256);
/// @notice Returns total number of node operators
function getNodeOperatorsCount() external view returns (uint256);
/// @notice Returns number of active node operators
function getActiveNodeOperatorsCount() external view returns (uint256);
/// @notice Returns if the node operator with given id is active
/// @param _nodeOperatorId Id of the node operator
function getNodeOperatorIsActive(uint256 _nodeOperatorId) external view returns (bool);
/// @notice Returns up to `_limit` node operator ids starting from the `_offset`. The order of
/// the returned ids is not defined and might change between calls.
/// @dev This view must not revert in case of invalid data passed. When `_offset` exceeds the
/// total node operators count or when `_limit` is equal to 0 MUST be returned empty array.
function getNodeOperatorIds(uint256 _offset, uint256 _limit)
external
view
returns (uint256[] memory nodeOperatorIds);
/// @notice Called by StakingRouter to signal that stETH rewards were minted for this module.
/// @param _totalShares Amount of stETH shares that were minted to reward all node operators.
/// @dev IMPORTANT: this method SHOULD revert with empty error data ONLY because of "out of gas".
/// Details about error data: https://docs.soliditylang.org/en/v0.8.9/control-structures.html#error-handling-assert-require-revert-and-exceptions
function onRewardsMinted(uint256 _totalShares) external;
/// @notice Updates the number of the validators of the given node operator that were requested
/// to exit but failed to do so in the max allowed time
/// @param _nodeOperatorIds bytes packed array of the node operators id
/// @param _stuckValidatorsCounts bytes packed array of the new number of STUCK validators for the node operators
function updateStuckValidatorsCount(
bytes calldata _nodeOperatorIds,
bytes calldata _stuckValidatorsCounts
) external;
/// @notice Updates the number of the validators in the EXITED state for node operator with given id
/// @param _nodeOperatorIds bytes packed array of the node operators id
/// @param _stuckValidatorsCounts bytes packed array of the new number of EXITED validators for the node operators
function updateExitedValidatorsCount(
bytes calldata _nodeOperatorIds,
bytes calldata _stuckValidatorsCounts
) external;
/// @notice Updates the number of the refunded validators for node operator with the given id
/// @param _nodeOperatorId Id of the node operator
/// @param _refundedValidatorsCount New number of refunded validators of the node operator
function updateRefundedValidatorsCount(uint256 _nodeOperatorId, uint256 _refundedValidatorsCount) external;
/// @notice Updates the limit of the validators that can be used for deposit
/// @param _nodeOperatorId Id of the node operator
/// @param _isTargetLimitActive Active flag
/// @param _targetLimit Target limit of the node operator
function updateTargetValidatorsLimits(
uint256 _nodeOperatorId,
bool _isTargetLimitActive,
uint256 _targetLimit
) external;
/// @notice Unsafely updates the number of validators in the EXITED/STUCK states for node operator with given id
/// 'unsafely' means that this method can both increase and decrease exited and stuck counters
/// @param _nodeOperatorId Id of the node operator
/// @param _exitedValidatorsCount New number of EXITED validators for the node operator
/// @param _stuckValidatorsCount New number of STUCK validator for the node operator
function unsafeUpdateValidatorsCount(
uint256 _nodeOperatorId,
uint256 _exitedValidatorsCount,
uint256 _stuckValidatorsCount
) external;
/// @notice Obtains deposit data to be used by StakingRouter to deposit to the Ethereum Deposit
/// contract
/// @dev The method MUST revert when the staking module has not enough deposit data items
/// @param _depositsCount Number of deposits to be done
/// @param _depositCalldata Staking module defined data encoded as bytes.
/// IMPORTANT: _depositCalldata MUST NOT modify the deposit data set of the staking module
/// @return publicKeys Batch of the concatenated public validators keys
/// @return signatures Batch of the concatenated deposit signatures for returned public keys
function obtainDepositData(uint256 _depositsCount, bytes calldata _depositCalldata)
external
returns (bytes memory publicKeys, bytes memory signatures);
/// @notice Called by StakingRouter after it finishes updating exited and stuck validators
/// counts for this module's node operators.
///
/// Guaranteed to be called after an oracle report is applied, regardless of whether any node
/// operator in this module has actually received any updated counts as a result of the report
/// but given that the total number of exited validators returned from getStakingModuleSummary
/// is the same as StakingRouter expects based on the total count received from the oracle.
///
/// @dev IMPORTANT: this method SHOULD revert with empty error data ONLY because of "out of gas".
/// Details about error data: https://docs.soliditylang.org/en/v0.8.9/control-structures.html#error-handling-assert-require-revert-and-exceptions
function onExitedAndStuckValidatorsCountsUpdated() external;
/// @notice Called by StakingRouter when withdrawal credentials are changed.
/// @dev This method MUST discard all StakingModule's unused deposit data cause they become
/// invalid after the withdrawal credentials are changed
///
/// @dev IMPORTANT: this method SHOULD revert with empty error data ONLY because of "out of gas".
/// Details about error data: https://docs.soliditylang.org/en/v0.8.9/control-structures.html#error-handling-assert-require-revert-and-exceptions
function onWithdrawalCredentialsChanged() external;
/// @dev Event to be emitted on StakingModule's nonce change
event NonceChanged(uint256 nonce);
}
contracts/0.8.9/lib/UnstructuredStorage.sol
/*
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.9;
/**
* @notice Aragon Unstructured Storage library
*/
library UnstructuredStorage {
function getStorageBool(bytes32 position) internal view returns (bool data) {
assembly { data := sload(position) }
}
function getStorageAddress(bytes32 position) internal view returns (address data) {
assembly { data := sload(position) }
}
function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) {
assembly { data := sload(position) }
}
function getStorageUint256(bytes32 position) internal view returns (uint256 data) {
assembly { data := sload(position) }
}
function setStorageBool(bytes32 position, bool data) internal {
assembly { sstore(position, data) }
}
function setStorageAddress(bytes32 position, address data) internal {
assembly { sstore(position, data) }
}
function setStorageBytes32(bytes32 position, bytes32 data) internal {
assembly { sstore(position, data) }
}
function setStorageUint256(bytes32 position, uint256 data) internal {
assembly { sstore(position, data) }
}
}
contracts/0.8.9/utils/Versioned.sol
// SPDX-FileCopyrightText: 2022 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.9;
import "../lib/UnstructuredStorage.sol";
contract Versioned {
using UnstructuredStorage for bytes32;
event ContractVersionSet(uint256 version);
error NonZeroContractVersionOnInit();
error InvalidContractVersionIncrement();
error UnexpectedContractVersion(uint256 expected, uint256 received);
/// @dev Storage slot: uint256 version
/// Version of the initialized contract storage.
/// The version stored in CONTRACT_VERSION_POSITION equals to:
/// - 0 right after the deployment, before an initializer is invoked (and only at that moment);
/// - N after calling initialize(), where N is the initially deployed contract version;
/// - N after upgrading contract by calling finalizeUpgrade_vN().
bytes32 internal constant CONTRACT_VERSION_POSITION = keccak256("lido.Versioned.contractVersion");
uint256 internal constant PETRIFIED_VERSION_MARK = type(uint256).max;
constructor() {
// lock version in the implementation's storage to prevent initialization
CONTRACT_VERSION_POSITION.setStorageUint256(PETRIFIED_VERSION_MARK);
}
/// @notice Returns the current contract version.
function getContractVersion() public view returns (uint256) {
return CONTRACT_VERSION_POSITION.getStorageUint256();
}
function _checkContractVersion(uint256 version) internal view {
uint256 expectedVersion = getContractVersion();
if (version != expectedVersion) {
revert UnexpectedContractVersion(expectedVersion, version);
}
}
/// @dev Sets the contract version to N. Should be called from the initialize() function.
function _initializeContractVersionTo(uint256 version) internal {
if (getContractVersion() != 0) revert NonZeroContractVersionOnInit();
_setContractVersion(version);
}
/// @dev Updates the contract version. Should be called from a finalizeUpgrade_vN() function.
function _updateContractVersion(uint256 newVersion) internal {
if (newVersion != getContractVersion() + 1) revert InvalidContractVersionIncrement();
_setContractVersion(newVersion);
}
function _setContractVersion(uint256 version) private {
CONTRACT_VERSION_POSITION.setStorageUint256(version);
emit ContractVersionSet(version);
}
}
contracts/0.8.9/utils/access/AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)
//
// A modified AccessControl contract using unstructured storage. Copied from tree:
// https://github.com/OpenZeppelin/openzeppelin-contracts/tree/6bd6b76/contracts/access
//
/* See contracts/COMPILERS.md */
pragma solidity 0.8.9;
import "@openzeppelin/contracts-v4.4/access/IAccessControl.sol";
import "@openzeppelin/contracts-v4.4/utils/Context.sol";
import "@openzeppelin/contracts-v4.4/utils/Strings.sol";
import "@openzeppelin/contracts-v4.4/utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
/// @dev Storage slot: mapping(bytes32 => RoleData) _roles
bytes32 private constant ROLES_POSITION = keccak256("openzeppelin.AccessControl._roles");
function _storageRoles() private pure returns (mapping(bytes32 => RoleData) storage _roles) {
bytes32 position = ROLES_POSITION;
assembly { _roles.slot := position }
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view override returns (bool) {
return _storageRoles()[role].members[account];
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
return _storageRoles()[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_storageRoles()[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_storageRoles()[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_storageRoles()[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
contracts/0.8.9/utils/access/AccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)
//
// A modified AccessControlEnumerable contract using unstructured storage. Copied from tree:
// https://github.com/OpenZeppelin/openzeppelin-contracts/tree/6bd6b76/contracts/access
//
/* See contracts/COMPILERS.md */
pragma solidity 0.8.9;
import "@openzeppelin/contracts-v4.4/access/IAccessControlEnumerable.sol";
import "@openzeppelin/contracts-v4.4/utils/structs/EnumerableSet.sol";
import "./AccessControl.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
/// @dev Storage slot: mapping(bytes32 => EnumerableSet.AddressSet) _roleMembers
bytes32 private constant ROLE_MEMBERS_POSITION = keccak256("openzeppelin.AccessControlEnumerable._roleMembers");
function _storageRoleMembers() private pure returns (
mapping(bytes32 => EnumerableSet.AddressSet) storage _roleMembers
) {
bytes32 position = ROLE_MEMBERS_POSITION;
assembly { _roleMembers.slot := position }
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {
return _storageRoleMembers()[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view override returns (uint256) {
return _storageRoleMembers()[role].length();
}
/**
* @dev Overload {_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_storageRoleMembers()[role].add(account);
}
/**
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_storageRoleMembers()[role].remove(account);
}
}
contracts/common/lib/Math256.sol
// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: MIT
// Copied from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0457042d93d9dfd760dbaa06a4d2f1216fdbe297/contracts/utils/math/Math.sol
// See contracts/COMPILERS.md
// solhint-disable-next-line
pragma solidity >=0.4.24 <0.9.0;
library Math256 {
/// @dev Returns the largest of two numbers.
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/// @dev Returns the smallest of two numbers.
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/// @dev Returns the largest of two numbers.
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/// @dev Returns the smallest of two numbers.
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/// @dev Returns the ceiling of the division of two numbers.
///
/// This differs from standard division with `/` in that it rounds up instead
/// of rounding down.
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/// @dev Returns absolute difference of two numbers.
function absDiff(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a - b : b - a;
}
}
contracts/common/lib/MemUtils.sol
// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
/* See contracts/COMPILERS.md */
// solhint-disable-next-line lido/fixed-compiler-version
pragma solidity >=0.4.24 <0.9.0;
library MemUtils {
/**
* @dev Allocates a memory byte array of `_len` bytes without zeroing it out.
*/
function unsafeAllocateBytes(uint256 _len) internal pure returns (bytes memory result) {
assembly {
result := mload(0x40)
mstore(result, _len)
let freeMemPtr := add(add(result, 32), _len)
// align free mem ptr to 32 bytes as the compiler does now
mstore(0x40, and(add(freeMemPtr, 31), not(31)))
}
}
/**
* Performs a memory copy of `_len` bytes from position `_src` to position `_dst`.
*/
function memcpy(uint256 _src, uint256 _dst, uint256 _len) internal pure {
assembly {
// while al least 32 bytes left, copy in 32-byte chunks
for { } gt(_len, 31) { } {
mstore(_dst, mload(_src))
_src := add(_src, 32)
_dst := add(_dst, 32)
_len := sub(_len, 32)
}
if gt(_len, 0) {
// read the next 32-byte chunk from _dst, replace the first N bytes
// with those left in the _src, and write the transformed chunk back
let mask := sub(shl(mul(8, sub(32, _len)), 1), 1) // 2 ** (8 * (32 - _len)) - 1
let srcMasked := and(mload(_src), not(mask))
let dstMasked := and(mload(_dst), mask)
mstore(_dst, or(dstMasked, srcMasked))
}
}
}
/**
* Copies `_len` bytes from `_src`, starting at position `_srcStart`, into `_dst`, starting at position `_dstStart` into `_dst`.
*/
function copyBytes(bytes memory _src, bytes memory _dst, uint256 _srcStart, uint256 _dstStart, uint256 _len) internal pure {
require(_srcStart + _len <= _src.length && _dstStart + _len <= _dst.length, "BYTES_ARRAY_OUT_OF_BOUNDS");
uint256 srcStartPos;
uint256 dstStartPos;
assembly {
srcStartPos := add(add(_src, 32), _srcStart)
dstStartPos := add(add(_dst, 32), _dstStart)
}
memcpy(srcStartPos, dstStartPos, _len);
}
/**
* Copies bytes from `_src` to `_dst`, starting at position `_dstStart` into `_dst`.
*/
function copyBytes(bytes memory _src, bytes memory _dst, uint256 _dstStart) internal pure {
copyBytes(_src, _dst, 0, _dstStart, _src.length);
}
}
contracts/common/lib/MinFirstAllocationStrategy.sol
// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
/* See contracts/COMPILERS.md */
// solhint-disable-next-line
pragma solidity >=0.4.24 <0.9.0;
import {Math256} from "./Math256.sol";
/// @notice Library with methods to calculate "proportional" allocations among buckets with different
/// capacity and level of filling.
/// @dev The current implementation favors buckets with the least fill factor
library MinFirstAllocationStrategy {
uint256 private constant MAX_UINT256 = 2**256 - 1;
/// @notice Allocates passed maxAllocationSize among the buckets. The resulting allocation doesn't exceed the
/// capacities of the buckets. An algorithm starts filling from the least populated buckets to equalize the fill factor.
/// For example, for buckets: [9998, 70, 0], capacities: [10000, 101, 100], and maxAllocationSize: 101, the allocation happens
/// following way:
/// 1. top up the bucket with index 2 on 70. Intermediate state of the buckets: [9998, 70, 70]. According to the definition,
/// the rest allocation must be proportionally split among the buckets with the same values.
/// 2. top up the bucket with index 1 on 15. Intermediate state of the buckets: [9998, 85, 70].
/// 3. top up the bucket with index 2 on 15. Intermediate state of the buckets: [9998, 85, 85].
/// 4. top up the bucket with index 1 on 1. Nothing to distribute. The final state of the buckets: [9998, 86, 85]
/// @dev Method modifies the passed buckets array to reduce the gas costs on memory allocation.
/// @param buckets The array of current allocations in the buckets
/// @param capacities The array of capacities of the buckets
/// @param allocationSize The desired value to allocate among the buckets
/// @return allocated The total value allocated among the buckets. Can't exceed the allocationSize value
function allocate(
uint256[] memory buckets,
uint256[] memory capacities,
uint256 allocationSize
) internal pure returns (uint256 allocated) {
uint256 allocatedToBestCandidate = 0;
while (allocated < allocationSize) {
allocatedToBestCandidate = allocateToBestCandidate(buckets, capacities, allocationSize - allocated);
if (allocatedToBestCandidate == 0) {
break;
}
allocated += allocatedToBestCandidate;
}
}
/// @notice Allocates the max allowed value not exceeding allocationSize to the bucket with the least value.
/// The candidate search happens according to the following algorithm:
/// 1. Find the first least filled bucket which has free space. Count the number of such buckets.
/// 2. If no buckets are found terminate the search - no free buckets
/// 3. Find the first bucket with free space, which has the least value greater
/// than the bucket found in step 1. To preserve proportional allocation the resulting allocation can't exceed this value.
/// 4. Calculate the allocation size as:
/// min(
/// (count of least filling buckets > 1 ? ceilDiv(allocationSize, count of least filling buckets) : allocationSize),
/// fill factor of the bucket found in step 3,
/// free space of the least filled bucket
/// )
/// @dev Method modifies the passed buckets array to reduce the gas costs on memory allocation.
/// @param buckets The array of current allocations in the buckets
/// @param capacities The array of capacities of the buckets
/// @param allocationSize The desired value to allocate to the bucket
/// @return allocated The total value allocated to the bucket. Can't exceed the allocationSize value
function allocateToBestCandidate(
uint256[] memory buckets,
uint256[] memory capacities,
uint256 allocationSize
) internal pure returns (uint256 allocated) {
uint256 bestCandidateIndex = buckets.length;
uint256 bestCandidateAllocation = MAX_UINT256;
uint256 bestCandidatesCount = 0;
if (allocationSize == 0) {
return 0;
}
for (uint256 i = 0; i < buckets.length; ++i) {
if (buckets[i] >= capacities[i]) {
continue;
} else if (bestCandidateAllocation > buckets[i]) {
bestCandidateIndex = i;
bestCandidatesCount = 1;
bestCandidateAllocation = buckets[i];
} else if (bestCandidateAllocation == buckets[i]) {
bestCandidatesCount += 1;
}
}
if (bestCandidatesCount == 0) {
return 0;
}
// cap the allocation by the smallest larger allocation than the found best one
uint256 allocationSizeUpperBound = MAX_UINT256;
for (uint256 j = 0; j < buckets.length; ++j) {
if (buckets[j] >= capacities[j]) {
continue;
} else if (buckets[j] > bestCandidateAllocation && buckets[j] < allocationSizeUpperBound) {
allocationSizeUpperBound = buckets[j];
}
}
allocated = Math256.min(
bestCandidatesCount > 1 ? Math256.ceilDiv(allocationSize, bestCandidatesCount) : allocationSize,
Math256.min(allocationSizeUpperBound, capacities[bestCandidateIndex]) - bestCandidateAllocation
);
buckets[bestCandidateIndex] += allocated;
}
}
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":150,"enabled":true},"libraries":{},"evmVersion":"istanbul"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_depositContract","internalType":"address"}]},{"type":"error","name":"AppAuthLidoFailed","inputs":[]},{"type":"error","name":"ArraysLengthMismatch","inputs":[{"type":"uint256","name":"firstArrayLength","internalType":"uint256"},{"type":"uint256","name":"secondArrayLength","internalType":"uint256"}]},{"type":"error","name":"DepositContractZeroAddress","inputs":[]},{"type":"error","name":"DirectETHTransfer","inputs":[]},{"type":"error","name":"EmptyWithdrawalsCredentials","inputs":[]},{"type":"error","name":"ExitedValidatorsCountCannotDecrease","inputs":[]},{"type":"error","name":"InvalidContractVersionIncrement","inputs":[]},{"type":"error","name":"InvalidDepositsValue","inputs":[{"type":"uint256","name":"etherValue","internalType":"uint256"},{"type":"uint256","name":"depositsCount","internalType":"uint256"}]},{"type":"error","name":"InvalidPublicKeysBatchLength","inputs":[{"type":"uint256","name":"actual","internalType":"uint256"},{"type":"uint256","name":"expected","internalType":"uint256"}]},{"type":"error","name":"InvalidReportData","inputs":[{"type":"uint256","name":"code","internalType":"uint256"}]},{"type":"error","name":"InvalidSignaturesBatchLength","inputs":[{"type":"uint256","name":"actual","internalType":"uint256"},{"type":"uint256","name":"expected","internalType":"uint256"}]},{"type":"error","name":"NonZeroContractVersionOnInit","inputs":[]},{"type":"error","name":"ReportedExitedValidatorsExceedDeposited","inputs":[{"type":"uint256","name":"reportedExitedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"depositedValidatorsCount","internalType":"uint256"}]},{"type":"error","name":"StakingModuleAddressExists","inputs":[]},{"type":"error","name":"StakingModuleNotActive","inputs":[]},{"type":"error","name":"StakingModuleNotPaused","inputs":[]},{"type":"error","name":"StakingModuleStatusTheSame","inputs":[]},{"type":"error","name":"StakingModuleUnregistered","inputs":[]},{"type":"error","name":"StakingModuleWrongName","inputs":[]},{"type":"error","name":"StakingModulesLimitExceeded","inputs":[]},{"type":"error","name":"UnexpectedContractVersion","inputs":[{"type":"uint256","name":"expected","internalType":"uint256"},{"type":"uint256","name":"received","internalType":"uint256"}]},{"type":"error","name":"UnexpectedCurrentValidatorsCount","inputs":[{"type":"uint256","name":"currentModuleExitedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"currentNodeOpExitedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"currentNodeOpStuckValidatorsCount","internalType":"uint256"}]},{"type":"error","name":"UnrecoverableModuleError","inputs":[]},{"type":"error","name":"ValueOver100Percent","inputs":[{"type":"string","name":"field","internalType":"string"}]},{"type":"error","name":"ZeroAddress","inputs":[{"type":"string","name":"field","internalType":"string"}]},{"type":"event","name":"ContractVersionSet","inputs":[{"type":"uint256","name":"version","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ExitedAndStuckValidatorsCountsUpdateFailed","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"bytes","name":"lowLevelRevertData","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"RewardsMintedReportFailed","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"bytes","name":"lowLevelRevertData","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"previousAdminRole","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"newAdminRole","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"StakingModuleAdded","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"address","name":"stakingModule","internalType":"address","indexed":false},{"type":"string","name":"name","internalType":"string","indexed":false},{"type":"address","name":"createdBy","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"StakingModuleExitedValidatorsIncompleteReporting","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"uint256","name":"unreportedExitedValidatorsCount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"StakingModuleFeesSet","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"uint256","name":"stakingModuleFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"treasuryFee","internalType":"uint256","indexed":false},{"type":"address","name":"setBy","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"StakingModuleStatusSet","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"uint8","name":"status","internalType":"enum StakingRouter.StakingModuleStatus","indexed":false},{"type":"address","name":"setBy","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"StakingModuleTargetShareSet","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"uint256","name":"targetShare","internalType":"uint256","indexed":false},{"type":"address","name":"setBy","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"StakingRouterETHDeposited","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawalCredentialsSet","inputs":[{"type":"bytes32","name":"withdrawalCredentials","internalType":"bytes32","indexed":false},{"type":"address","name":"setBy","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawalsCredentialsChangeFailed","inputs":[{"type":"uint256","name":"stakingModuleId","internalType":"uint256","indexed":true},{"type":"bytes","name":"lowLevelRevertData","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DEFAULT_ADMIN_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IDepositContract"}],"name":"DEPOSIT_CONTRACT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FEE_PRECISION_POINTS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"MANAGE_WITHDRAWAL_CREDENTIALS_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_STAKING_MODULES_COUNT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_STAKING_MODULE_NAME_LENGTH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"REPORT_EXITED_VALIDATORS_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"REPORT_REWARDS_MINTED_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"STAKING_MODULE_MANAGE_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"STAKING_MODULE_PAUSE_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"STAKING_MODULE_RESUME_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TOTAL_BASIS_POINTS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"UNSAFE_SET_EXITED_VALIDATORS_ROLE","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addStakingModule","inputs":[{"type":"string","name":"_name","internalType":"string"},{"type":"address","name":"_stakingModuleAddress","internalType":"address"},{"type":"uint256","name":"_targetShare","internalType":"uint256"},{"type":"uint256","name":"_stakingModuleFee","internalType":"uint256"},{"type":"uint256","name":"_treasuryFee","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"deposit","inputs":[{"type":"uint256","name":"_depositsCount","internalType":"uint256"},{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"bytes","name":"_depositCalldata","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"","internalType":"struct StakingRouter.NodeOperatorDigest[]","components":[{"type":"uint256","name":"id","internalType":"uint256"},{"type":"bool","name":"isActive","internalType":"bool"},{"type":"tuple","name":"summary","internalType":"struct StakingRouter.NodeOperatorSummary","components":[{"type":"bool","name":"isTargetLimitActive","internalType":"bool"},{"type":"uint256","name":"targetValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"stuckValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"refundedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"stuckPenaltyEndTimestamp","internalType":"uint256"},{"type":"uint256","name":"totalExitedValidators","internalType":"uint256"},{"type":"uint256","name":"totalDepositedValidators","internalType":"uint256"},{"type":"uint256","name":"depositableValidatorsCount","internalType":"uint256"}]}]}],"name":"getAllNodeOperatorDigests","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"","internalType":"struct StakingRouter.StakingModuleDigest[]","components":[{"type":"uint256","name":"nodeOperatorsCount","internalType":"uint256"},{"type":"uint256","name":"activeNodeOperatorsCount","internalType":"uint256"},{"type":"tuple","name":"state","internalType":"struct StakingRouter.StakingModule","components":[{"type":"uint24","name":"id","internalType":"uint24"},{"type":"address","name":"stakingModuleAddress","internalType":"address"},{"type":"uint16","name":"stakingModuleFee","internalType":"uint16"},{"type":"uint16","name":"treasuryFee","internalType":"uint16"},{"type":"uint16","name":"targetShare","internalType":"uint16"},{"type":"uint8","name":"status","internalType":"uint8"},{"type":"string","name":"name","internalType":"string"},{"type":"uint64","name":"lastDepositAt","internalType":"uint64"},{"type":"uint256","name":"lastDepositBlock","internalType":"uint256"},{"type":"uint256","name":"exitedValidatorsCount","internalType":"uint256"}]},{"type":"tuple","name":"summary","internalType":"struct StakingRouter.StakingModuleSummary","components":[{"type":"uint256","name":"totalExitedValidators","internalType":"uint256"},{"type":"uint256","name":"totalDepositedValidators","internalType":"uint256"},{"type":"uint256","name":"depositableValidatorsCount","internalType":"uint256"}]}]}],"name":"getAllStakingModuleDigests","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getContractVersion","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"allocated","internalType":"uint256"},{"type":"uint256[]","name":"allocations","internalType":"uint256[]"}],"name":"getDepositsAllocation","inputs":[{"type":"uint256","name":"_depositsCount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getLido","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"digests","internalType":"struct StakingRouter.NodeOperatorDigest[]","components":[{"type":"uint256","name":"id","internalType":"uint256"},{"type":"bool","name":"isActive","internalType":"bool"},{"type":"tuple","name":"summary","internalType":"struct StakingRouter.NodeOperatorSummary","components":[{"type":"bool","name":"isTargetLimitActive","internalType":"bool"},{"type":"uint256","name":"targetValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"stuckValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"refundedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"stuckPenaltyEndTimestamp","internalType":"uint256"},{"type":"uint256","name":"totalExitedValidators","internalType":"uint256"},{"type":"uint256","name":"totalDepositedValidators","internalType":"uint256"},{"type":"uint256","name":"depositableValidatorsCount","internalType":"uint256"}]}]}],"name":"getNodeOperatorDigests","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint256[]","name":"_nodeOperatorIds","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"","internalType":"struct StakingRouter.NodeOperatorDigest[]","components":[{"type":"uint256","name":"id","internalType":"uint256"},{"type":"bool","name":"isActive","internalType":"bool"},{"type":"tuple","name":"summary","internalType":"struct StakingRouter.NodeOperatorSummary","components":[{"type":"bool","name":"isTargetLimitActive","internalType":"bool"},{"type":"uint256","name":"targetValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"stuckValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"refundedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"stuckPenaltyEndTimestamp","internalType":"uint256"},{"type":"uint256","name":"totalExitedValidators","internalType":"uint256"},{"type":"uint256","name":"totalDepositedValidators","internalType":"uint256"},{"type":"uint256","name":"depositableValidatorsCount","internalType":"uint256"}]}]}],"name":"getNodeOperatorDigests","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint256","name":"_offset","internalType":"uint256"},{"type":"uint256","name":"_limit","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"summary","internalType":"struct StakingRouter.NodeOperatorSummary","components":[{"type":"bool","name":"isTargetLimitActive","internalType":"bool"},{"type":"uint256","name":"targetValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"stuckValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"refundedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"stuckPenaltyEndTimestamp","internalType":"uint256"},{"type":"uint256","name":"totalExitedValidators","internalType":"uint256"},{"type":"uint256","name":"totalDepositedValidators","internalType":"uint256"},{"type":"uint256","name":"depositableValidatorsCount","internalType":"uint256"}]}],"name":"getNodeOperatorSummary","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint256","name":"_nodeOperatorId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getRoleAdmin","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getRoleMember","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"uint256","name":"index","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRoleMemberCount","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint96","name":"modulesFee","internalType":"uint96"},{"type":"uint96","name":"treasuryFee","internalType":"uint96"},{"type":"uint256","name":"basePrecision","internalType":"uint256"}],"name":"getStakingFeeAggregateDistribution","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"modulesFee","internalType":"uint16"},{"type":"uint16","name":"treasuryFee","internalType":"uint16"}],"name":"getStakingFeeAggregateDistributionE4Precision","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct StakingRouter.StakingModule","components":[{"type":"uint24","name":"id","internalType":"uint24"},{"type":"address","name":"stakingModuleAddress","internalType":"address"},{"type":"uint16","name":"stakingModuleFee","internalType":"uint16"},{"type":"uint16","name":"treasuryFee","internalType":"uint16"},{"type":"uint16","name":"targetShare","internalType":"uint16"},{"type":"uint8","name":"status","internalType":"uint8"},{"type":"string","name":"name","internalType":"string"},{"type":"uint64","name":"lastDepositAt","internalType":"uint64"},{"type":"uint256","name":"lastDepositBlock","internalType":"uint256"},{"type":"uint256","name":"exitedValidatorsCount","internalType":"uint256"}]}],"name":"getStakingModule","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"activeValidatorsCount","internalType":"uint256"}],"name":"getStakingModuleActiveValidatorsCount","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"digests","internalType":"struct StakingRouter.StakingModuleDigest[]","components":[{"type":"uint256","name":"nodeOperatorsCount","internalType":"uint256"},{"type":"uint256","name":"activeNodeOperatorsCount","internalType":"uint256"},{"type":"tuple","name":"state","internalType":"struct StakingRouter.StakingModule","components":[{"type":"uint24","name":"id","internalType":"uint24"},{"type":"address","name":"stakingModuleAddress","internalType":"address"},{"type":"uint16","name":"stakingModuleFee","internalType":"uint16"},{"type":"uint16","name":"treasuryFee","internalType":"uint16"},{"type":"uint16","name":"targetShare","internalType":"uint16"},{"type":"uint8","name":"status","internalType":"uint8"},{"type":"string","name":"name","internalType":"string"},{"type":"uint64","name":"lastDepositAt","internalType":"uint64"},{"type":"uint256","name":"lastDepositBlock","internalType":"uint256"},{"type":"uint256","name":"exitedValidatorsCount","internalType":"uint256"}]},{"type":"tuple","name":"summary","internalType":"struct StakingRouter.StakingModuleSummary","components":[{"type":"uint256","name":"totalExitedValidators","internalType":"uint256"},{"type":"uint256","name":"totalDepositedValidators","internalType":"uint256"},{"type":"uint256","name":"depositableValidatorsCount","internalType":"uint256"}]}]}],"name":"getStakingModuleDigests","inputs":[{"type":"uint256[]","name":"_stakingModuleIds","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"stakingModuleIds","internalType":"uint256[]"}],"name":"getStakingModuleIds","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"getStakingModuleIsActive","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"getStakingModuleIsDepositsPaused","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"getStakingModuleIsStopped","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getStakingModuleLastDepositBlock","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getStakingModuleMaxDepositsCount","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint256","name":"_maxDepositsValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getStakingModuleNonce","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"enum StakingRouter.StakingModuleStatus"}],"name":"getStakingModuleStatus","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"summary","internalType":"struct StakingRouter.StakingModuleSummary","components":[{"type":"uint256","name":"totalExitedValidators","internalType":"uint256"},{"type":"uint256","name":"totalDepositedValidators","internalType":"uint256"},{"type":"uint256","name":"depositableValidatorsCount","internalType":"uint256"}]}],"name":"getStakingModuleSummary","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"res","internalType":"struct StakingRouter.StakingModule[]","components":[{"type":"uint24","name":"id","internalType":"uint24"},{"type":"address","name":"stakingModuleAddress","internalType":"address"},{"type":"uint16","name":"stakingModuleFee","internalType":"uint16"},{"type":"uint16","name":"treasuryFee","internalType":"uint16"},{"type":"uint16","name":"targetShare","internalType":"uint16"},{"type":"uint8","name":"status","internalType":"uint8"},{"type":"string","name":"name","internalType":"string"},{"type":"uint64","name":"lastDepositAt","internalType":"uint64"},{"type":"uint256","name":"lastDepositBlock","internalType":"uint256"},{"type":"uint256","name":"exitedValidatorsCount","internalType":"uint256"}]}],"name":"getStakingModules","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getStakingModulesCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"recipients","internalType":"address[]"},{"type":"uint256[]","name":"stakingModuleIds","internalType":"uint256[]"},{"type":"uint96[]","name":"stakingModuleFees","internalType":"uint96[]"},{"type":"uint96","name":"totalFee","internalType":"uint96"},{"type":"uint256","name":"precisionPoints","internalType":"uint256"}],"name":"getStakingRewardsDistribution","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"totalFee","internalType":"uint16"}],"name":"getTotalFeeE4Precision","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getWithdrawalCredentials","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasStakingModule","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_admin","internalType":"address"},{"type":"address","name":"_lido","internalType":"address"},{"type":"bytes32","name":"_withdrawalCredentials","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"onValidatorsCountsByNodeOperatorReportingFinished","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pauseStakingModule","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reportRewardsMinted","inputs":[{"type":"uint256[]","name":"_stakingModuleIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_totalShares","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reportStakingModuleExitedValidatorsCountByNodeOperator","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"bytes","name":"_nodeOperatorIds","internalType":"bytes"},{"type":"bytes","name":"_exitedValidatorsCounts","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reportStakingModuleStuckValidatorsCountByNodeOperator","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"bytes","name":"_nodeOperatorIds","internalType":"bytes"},{"type":"bytes","name":"_stuckValidatorsCounts","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"resumeStakingModule","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setStakingModuleStatus","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint8","name":"_status","internalType":"enum StakingRouter.StakingModuleStatus"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setWithdrawalCredentials","inputs":[{"type":"bytes32","name":"_withdrawalCredentials","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"supportsInterface","inputs":[{"type":"bytes4","name":"interfaceId","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unsafeSetExitedValidatorsCount","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint256","name":"_nodeOperatorId","internalType":"uint256"},{"type":"bool","name":"_triggerUpdateFinish","internalType":"bool"},{"type":"tuple","name":"_correction","internalType":"struct StakingRouter.ValidatorsCountsCorrection","components":[{"type":"uint256","name":"currentModuleExitedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"currentNodeOperatorExitedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"currentNodeOperatorStuckValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"newModuleExitedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"newNodeOperatorExitedValidatorsCount","internalType":"uint256"},{"type":"uint256","name":"newNodeOperatorStuckValidatorsCount","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"updateExitedValidatorsCountByStakingModule","inputs":[{"type":"uint256[]","name":"_stakingModuleIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_exitedValidatorsCounts","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateRefundedValidatorsCount","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint256","name":"_nodeOperatorId","internalType":"uint256"},{"type":"uint256","name":"_refundedValidatorsCount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateStakingModule","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint256","name":"_targetShare","internalType":"uint256"},{"type":"uint256","name":"_stakingModuleFee","internalType":"uint256"},{"type":"uint256","name":"_treasuryFee","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateTargetValidatorsLimits","inputs":[{"type":"uint256","name":"_stakingModuleId","internalType":"uint256"},{"type":"uint256","name":"_nodeOperatorId","internalType":"uint256"},{"type":"bool","name":"_isTargetLimitActive","internalType":"bool"},{"type":"uint256","name":"_targetLimit","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
Contract Creation Code
0x60a06040523480156200001157600080fd5b5060405162006099380380620060998339810160408190526200003491620000ae565b806001600160a01b0381166200005d57604051637c5f8bcf60e11b815260040160405180910390fd5b6001600160a01b0316608052620000a37f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6600019620000aa602090811b6200374e17901c565b50620000e0565b9055565b600060208284031215620000c157600080fd5b81516001600160a01b0381168114620000d957600080fd5b9392505050565b608051615f9662000103600039600081816107540152613d260152615f966000f3fe6080604052600436106103d25760003560e01c80639010d07c116101fd578063c82b1bb111610118578063e016e6f7116100ab578063efcdcc0e1161007a578063efcdcc0e14610c4a578063f07ff28a14610c7a578063f2aebb6514610c9a578063f8bb6d4214610cbc578063fa5093eb14610cdc57600080fd5b8063e016e6f714610bc8578063e1b92a5c14610bea578063e24ce9f114610c0a578063e97ee8cc14610c2a57600080fd5b8063d0a2b1b8116100e7578063d0a2b1b814610b53578063d547741f14610b73578063d861c58414610b93578063db3c7ba714610bb357600080fd5b8063c82b1bb114610ac5578063c8ac498014610af3578063ca15c87314610b13578063cb589b9a14610b3357600080fd5b8063a7357c8c11610190578063af1240971161015f578063af12409714610a30578063ba21ccae14610a50578063bc1bb19014610a76578063c445ea7514610aa357600080fd5b8063a7357c8c1461099d578063aa0b7db7146109d0578063aa5a1b9d146109e3578063abd44a2414610a1057600080fd5b80639fbb7bae116101cc5780639fbb7bae146108e55780639fc5a6ed1461090d578063a217fddf1461093a578063a734329c1461094f57600080fd5b80639010d07c1461087057806391d148541461089057806396b5d81c146108b05780639b75b4ef146108d057600080fd5b806356396715116102ed5780636b96736b116102805780638525e3a11161024f5780638525e3a1146107e75780638801da79146108075780638aa104351461083b5780638dc70c571461085057600080fd5b80636b96736b146107425780637443f523146107765780637a74884d146107965780637c8da51c146107ca57600080fd5b80636183214d116102bc5780636183214d146106b35780636608b11b146106d55780636a516b47146106f55780636ada55b91461072257600080fd5b8063563967151461063c57806357993b85146106515780635bf55e40146106735780636133f9851461069357600080fd5b8063271662ec116103655780633e54ee5b116103345780633e54ee5b146105d2578063473e0433146105f25780634a7583b6146106125780634b3a1cb71461062757600080fd5b8063271662ec1461054f5780632f2ff15d146105655780633240a3221461058557806336568abe146105b257600080fd5b80631565d2f2116103a15780631565d2f2146104a757806319c64b79146104db5780631d1b9d3c146104fb578063248a9ca31461052f57600080fd5b806301ffc9a7146103f55780630519fbbf1461042a578063072859c71461045857806307e203ac1461047a57600080fd5b366103f0576040516309fb455960e41b815260040160405180910390fd5b600080fd5b34801561040157600080fd5b50610415610410366004614e56565b610d17565b60405190151581526020015b60405180910390f35b34801561043657600080fd5b5061044a610445366004614e80565b610d42565b604051908152602001610421565b34801561046457600080fd5b50610478610473366004614f15565b610dbd565b005b34801561048657600080fd5b5061049a610495366004614e80565b610fcc565b6040516104219190614faa565b3480156104b357600080fd5b5061044a7f55180e25fcacf9af017d35d497765476319b23896daa1f9bc2b38fa80b36a16381565b3480156104e757600080fd5b5061044a6104f6366004614fcb565b61108b565b34801561050757600080fd5b5061044a7f779e5c23cb7a5bcb9bfe1e9a5165a00057f12bcdfd13e374540fdf1a1cd9113781565b34801561053b57600080fd5b5061044a61054a366004614e80565b61110a565b34801561055b57600080fd5b5061044a61271081565b34801561057157600080fd5b50610478610580366004615009565b61112c565b34801561059157600080fd5b506105a56105a0366004614e80565b61114e565b6040516104219190615085565b3480156105be57600080fd5b506104786105cd366004615009565b6111e6565b3480156105de57600080fd5b506104786105ed366004615133565b611264565b3480156105fe57600080fd5b5061044a61060d366004614e80565b611687565b34801561061e57600080fd5b5061044a61169e565b34801561063357600080fd5b5061044a602081565b34801561064857600080fd5b5061044a6116cd565b34801561065d57600080fd5b506106666116f7565b60405161042191906152cd565b34801561067f57600080fd5b5061047861068e366004614e80565b611704565b34801561069f57600080fd5b506104786106ae366004615372565b611794565b3480156106bf57600080fd5b506106c86118b8565b60405161042191906153ae565b3480156106e157600080fd5b506104156106f0366004614e80565b611a87565b34801561070157600080fd5b5061070a611aac565b6040516001600160a01b039091168152602001610421565b34801561072e57600080fd5b5061041561073d366004614e80565b611ad6565b34801561074e57600080fd5b5061070a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561078257600080fd5b50610478610791366004615410565b611adf565b3480156107a257600080fd5b5061044a7fe7c742a54cd11fc9749a47ab34bdcd7327820908e8d0d48b4a5c7f17b029409881565b3480156107d657600080fd5b5061044a68056bc75e2d6310000081565b3480156107f357600080fd5b506106666108023660046154d2565b611b7f565b34801561081357600080fd5b5061044a7f9a2f67efb89489040f2c48c3b2c38f719fba1276678d2ced3bd9049fb5edc6b281565b34801561084757600080fd5b5061044a611d69565b34801561085c57600080fd5b5061047861086b366004615506565b611d93565b34801561087c57600080fd5b5061070a61088b366004614fcb565b611f24565b34801561089c57600080fd5b506104156108ab366004615009565b611f50565b3480156108bc57600080fd5b5061044a6108cb366004614e80565b611f88565b3480156108dc57600080fd5b5061044a601f81565b3480156108f157600080fd5b506108fa612047565b60405161ffff9091168152602001610421565b34801561091957600080fd5b5061092d610928366004614e80565b612075565b6040516104219190615570565b34801561094657600080fd5b5061044a600081565b34801561095b57600080fd5b5061041561096a366004614e80565b60009081527f9b48f5b32acb95b982effe269feac267eead113c4b5af14ffeb9aadac18a6e9c6020526040902054151590565b3480156109a957600080fd5b5061044a7eb1e70095ba5bacc3202c3db9faf1f7873186f0ed7b6c84e80c0018dcc6e38e81565b6104786109de36600461557e565b61209c565b3480156109ef57600080fd5b50610a036109fe366004614fcb565b612304565b60405161042191906155d0565b348015610a1c57600080fd5b5061044a610a2b366004615623565b612423565b348015610a3c57600080fd5b50610478610a4b366004615623565b61267d565b348015610a5c57600080fd5b50610a65612853565b6040516104219594939291906156bd565b348015610a8257600080fd5b50610a96610a91366004614e80565b612bce565b604051610421919061577c565b348015610aaf57600080fd5b5061044a600080516020615f2183398151915281565b348015610ad157600080fd5b50610ae5610ae0366004614e80565b612d12565b60405161042192919061578f565b348015610aff57600080fd5b50610478610b0e3660046157a8565b612d2a565b348015610b1f57600080fd5b5061044a610b2e366004614e80565b612dcd565b348015610b3f57600080fd5b50610478610b4e3660046157a8565b612df1565b348015610b5f57600080fd5b50610478610b6e366004615821565b612e65565b348015610b7f57600080fd5b50610478610b8e366004615009565b612efa565b348015610b9f57600080fd5b50610478610bae366004614e80565b612f17565b348015610bbf57600080fd5b50610478612fa8565b348015610bd457600080fd5b5061044a600080516020615f4183398151915281565b348015610bf657600080fd5b50610478610c05366004615855565b613175565b348015610c1657600080fd5b50610415610c25366004614e80565b613201565b348015610c3657600080fd5b50610478610c45366004614e80565b61320a565b348015610c5657600080fd5b50610c5f6133c9565b6040805161ffff938416815292909116602083015201610421565b348015610c8657600080fd5b506105a5610c95366004615881565b613410565b348015610ca657600080fd5b50610caf6135a4565b60405161042191906158c7565b348015610cc857600080fd5b506105a5610cd7366004615855565b61363a565b348015610ce857600080fd5b50610cf16136dc565b604080516001600160601b03948516815293909216602084015290820152606001610421565b60006001600160e01b03198216635a05180f60e01b1480610d3c5750610d3c82613752565b92915050565b6000610d4d82613787565b6001600160a01b031663d087d2886040518163ffffffff1660e01b815260040160206040518083038186803b158015610d8557600080fd5b505afa158015610d99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3c91906158da565b7f55180e25fcacf9af017d35d497765476319b23896daa1f9bc2b38fa80b36a163610de881336137a9565b6000610df38661380d565b8054604051632cc1db0f60e21b815260048101889052919250630100000090046001600160a01b0316906000908190839063b3076c3c906024016101006040518083038186803b158015610e4657600080fd5b505afa158015610e5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7e91906158f3565b5050955050509350505083600401548660000151141580610ea3575080866020015114155b80610eb2575081866040015114155b15610ee95760048481015460405163e882688560e01b81529182015260248101829052604481018390526064015b60405180910390fd5b6060860151600480860191909155608087015160a088015160405163f2e2ca6360e01b81529283018b9052602483019190915260448201526001600160a01b0384169063f2e2ca6390606401600060405180830381600087803b158015610f4f57600080fd5b505af1158015610f63573d6000803e3d6000fd5b505050508615610fc157826001600160a01b031663e864299e6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610fa857600080fd5b505af1158015610fbc573d6000803e3d6000fd5b505050505b505050505050505050565b610ff060405180606001604052806000815260200160008152602001600081525090565b6000610ffb83612bce565b9050600081602001519050806001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b15801561103f57600080fd5b505afa158015611053573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611077919061595d565b604086015260208501528352509092915050565b600080806110ac6110a76a1a784379d99db420000000866159b7565b613820565b925092505060006110bc866139e5565b90508181815181106110d0576110d06159cb565b602002602001015160c001518382815181106110ee576110ee6159cb565b602002602001015161110091906159e1565b9695505050505050565b6000908152600080516020615f01833981519152602052604090206001015490565b6111358261110a565b61113f81336137a9565b6111498383613a3e565b505050565b6060600061115b83613787565b90506000816001600160a01b031663a70c70e46040518163ffffffff1660e01b815260040160206040518083038186803b15801561119857600080fd5b505afa1580156111ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d091906158da565b90506111de8460008361363a565b949350505050565b6001600160a01b03811633146112565760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610ee0565b6112608282613a6d565b5050565b600080516020615f4183398151915261127d81336137a9565b6127108411156112bf57604051630cb4392560e31b815260206004820152600c60248201526b5f746172676574536861726560a01b6044820152606401610ee0565b6127106112cc83856159f8565b111561131b57604051630cb4392560e31b815260206004820181905260248201527f5f7374616b696e674d6f64756c65466565202b205f74726561737572794665656044820152606401610ee0565b6001600160a01b03851661136a5760405163eac0d38960e01b81526020600482015260156024820152745f7374616b696e674d6f64756c654164647265737360581b6044820152606401610ee0565b8515806113775750601f86115b156113955760405163ac18716960e01b815260040160405180910390fd5b600061139f61169e565b9050602081106113c25760405163309eed9960e01b815260040160405180910390fd5b60005b81811015611412576113d681613a9c565b546001600160a01b03888116630100000090920416141561140a5760405163050f969d60e41b815260040160405180910390fd5b6001016113c5565b50600061141e82613a9c565b9050600061144a7ff9a85ae945d8134f58bd2ee028636634dcb9e812798acb5c806bf1951232a2255490565b611455906001615a10565b825462ffffff191662ffffff82161783559050611476600183018b8b614c70565b508154630100000065ffff0000000160b81b03191663010000006001600160a01b038a160261ffff60d81b191617600160d81b61ffff898116919091029190911763ffffffff60b81b1916600160b81b8883160261ffff60c81b191617600160c81b918716919091021760ff60e81b1916825560028201805467ffffffffffffffff1916426001600160401b03161790554360038301556040805160008152905162ffffff8316917f9151b7f88aca05d432bb395647ef52b2ffc454e3c6afb69c95345af6b5a778c0919081900360200190a26115588162ffffff1684613acc565b62ffffff81167ff9a85ae945d8134f58bd2ee028636634dcb9e812798acb5c806bf1951232a225556115b261158e8460016159f8565b7f1b3ef9db2d6f0727a31622833b45264c21051726d23ddb6f73b3b65628cafcc355565b8062ffffff167f43b5213f0e1666cd0b8692a73686164c94deb955a59c65e10dee8bb958e7ce3e898c8c336040516115ed9493929190615a60565b60405180910390a26040805188815233602082015262ffffff8316917f065e5bd8e4145dd99cf69bad5871ad52d094aee07a67fcf2f418c89e49d5f20c910160405180910390a260408051878152602081018790523381830152905162ffffff8316917f303c8ac43d1b1f9b898ddd2915a294efa01e9b07c322d7deeb7db332b66f0410919081900360600190a250505050505050505050565b6000806116938361380d565b600301549392505050565b60006116c87f1b3ef9db2d6f0727a31622833b45264c21051726d23ddb6f73b3b65628cafcc35490565b905090565b60006116c87fabeb05279af36da5d476d7f950157cd2ea98a4166fa68a6bc97ce3a22fbb93c05490565b60606116c86108026135a4565b7eb1e70095ba5bacc3202c3db9faf1f7873186f0ed7b6c84e80c0018dcc6e38e61172e81336137a9565b60006117398361380d565b905060008154600160e81b900460ff16600281111561175a5761175a615538565b600281111561176b5761176b615538565b146117895760405163322e64fb60e11b815260040160405180910390fd5b611149816001613b0f565b6001600160a01b0383166117d45760405163eac0d38960e01b81526020600482015260066024820152652fb0b236b4b760d11b6044820152606401610ee0565b6001600160a01b0382166118135760405163eac0d38960e01b81526020600482015260056024820152645f6c69646f60d81b6044820152606401610ee0565b61181d6001613bd2565b611828600084613c04565b6118517f706b9ed9846c161ad535be9b6345c3a7b2cb929e8d4a7254dee9ba6e6f8e5531839055565b61187a7fabeb05279af36da5d476d7f950157cd2ea98a4166fa68a6bc97ce3a22fbb93c0829055565b604080518281523360208201527f82e72df77173eab89b00556d791a407a78f4605c5c2f0694967c8c429dd43c7c91015b60405180910390a1505050565b606060006118c461169e565b9050806001600160401b038111156118de576118de614ea7565b60405190808252806020026020018201604052801561191757816020015b611904614cf4565b8152602001906001900390816118fc5790505b50915060005b81811015611a825761192e81613a9c565b6040805161014081018252825462ffffff81168252630100000081046001600160a01b03166020830152600160b81b810461ffff90811693830193909352600160c81b810483166060830152600160d81b81049092166080820152600160e81b90910460ff1660a082015260018201805491929160c0840191906119b190615a97565b80601f01602080910402602001604051908101604052809291908181526020018280546119dd90615a97565b8015611a2a5780601f106119ff57610100808354040283529160200191611a2a565b820191906000526020600020905b815481529060010190602001808311611a0d57829003601f168201915b505050918352505060028201546001600160401b03166020820152600382015460408201526004909101546060909101528351849083908110611a6f57611a6f6159cb565b602090810291909101015260010161191d565b505090565b6000805b611a9483612075565b6002811115611aa557611aa5615538565b1492915050565b60006116c87f706b9ed9846c161ad535be9b6345c3a7b2cb929e8d4a7254dee9ba6e6f8e55315490565b60006002611a8b565b600080516020615f41833981519152611af881336137a9565b6000611b038661380d565b546040516354f3d42360e11b81526004810187905285151560248201526044810185905263010000009091046001600160a01b03169150819063a9e7a84690606401600060405180830381600087803b158015611b5f57600080fd5b505af1158015611b73573d6000803e3d6000fd5b50505050505050505050565b606081516001600160401b03811115611b9a57611b9a614ea7565b604051908082528060200260200182016040528015611bd357816020015b611bc0614d47565b815260200190600190039081611bb85790505b50905060005b8251811015611d63576000611c06848381518110611bf957611bf96159cb565b6020026020010151612bce565b90506000816020015190506040518060800160405280826001600160a01b031663a70c70e46040518163ffffffff1660e01b815260040160206040518083038186803b158015611c5557600080fd5b505afa158015611c69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8d91906158da565b8152602001826001600160a01b0316638469cbd36040518163ffffffff1660e01b815260040160206040518083038186803b158015611ccb57600080fd5b505afa158015611cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d0391906158da565b8152602001838152602001611d30878681518110611d2357611d236159cb565b6020026020010151610fcc565b815250848481518110611d4557611d456159cb565b6020026020010181905250505080611d5c90615acc565b9050611bd9565b50919050565b60006116c87f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a65490565b600080516020615f41833981519152611dac81336137a9565b612710841115611dee57604051630cb4392560e31b815260206004820152600c60248201526b5f746172676574536861726560a01b6044820152606401610ee0565b612710611dfb83856159f8565b1115611e4a57604051630cb4392560e31b815260206004820181905260248201527f5f7374616b696e674d6f64756c65466565202b205f74726561737572794665656044820152606401610ee0565b6000611e558661380d565b805463ffffffff60c81b1916600160d81b61ffff8881169190910261ffff60c81b191691909117600160c81b868316021761ffff60b81b1916600160b81b918716919091021781556040805187815233602082015291925087917f065e5bd8e4145dd99cf69bad5871ad52d094aee07a67fcf2f418c89e49d5f20c910160405180910390a260408051858152602081018590523381830152905187917f303c8ac43d1b1f9b898ddd2915a294efa01e9b07c322d7deeb7db332b66f0410919081900360600190a2505050505050565b6000828152600080516020615ee183398151915260205260408120611f499083613c0e565b9392505050565b6000918252600080516020615f01833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600080611f948361380d565b90506000808260000160039054906101000a90046001600160a01b03166001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b158015611fe957600080fd5b505afa158015611ffd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612021919061595d565b5091509150612034836004015483613c1a565b61203e90826159e1565b95945050505050565b6000806000612054612853565b9450945050505061206e826001600160601b031682613c30565b9250505090565b60006120808261380d565b54600160e81b900460ff166002811115610d3c57610d3c615538565b7f706b9ed9846c161ad535be9b6345c3a7b2cb929e8d4a7254dee9ba6e6f8e5531546001600160a01b0316336001600160a01b0316146120ef57604051637e71782360e01b815260040160405180910390fd5b60006120f96116cd565b9050806121195760405163180a97cd60e21b815260040160405180910390fd5b60006121248561380d565b905060008154600160e81b900460ff16600281111561214557612145615538565b600281111561215657612156615538565b146121745760405163322e64fb60e11b815260040160405180910390fd5b60028101805467ffffffffffffffff1916426001600160401b0316179055436003820155604051348082529086907f9151b7f88aca05d432bb395647ef52b2ffc454e3c6afb69c95345af6b5a778c09060200160405180910390a26121e46a1a784379d99db42000000088615ae7565b811461220d5760405163023db95b60e21b81526004810182905260248101889052604401610ee0565b86156122fb5781546040516317dc836b60e31b8152600091829163010000009091046001600160a01b03169063bee41b5890612251908c908b908b90600401615b06565b600060405180830381600087803b15801561226b57600080fd5b505af115801561227f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122a79190810190615b83565b9150915060004790506122dd8a876040516020016122c791815260200190565b6040516020818303038152906040528585613c49565b47846122e982846159e1565b146122f6576122f6615bdc565b505050505b50505050505050565b61230c614d96565b600061231784612bce565b9050600081602001519050600080600080600080600080886001600160a01b031663b3076c3c8d6040518263ffffffff1660e01b815260040161235c91815260200190565b6101006040518083038186803b15801561237557600080fd5b505afa158015612389573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ad91906158f3565b97509750975097509750975097509750878b6000019015159081151581525050868b6020018181525050858b6040018181525050848b6060018181525050838b6080018181525050828b60a0018181525050818b60c0018181525050808b60e00181815250505050505050505050505092915050565b6000600080516020615f2183398151915261243e81336137a9565b8483146124685760405163098b37e560e31b81526004810186905260248101849052604401610ee0565b6000805b86811015612672576000888883818110612488576124886159cb565b905060200201359050600061249c8261380d565b6004810154909150808989868181106124b7576124b76159cb565b9050602002013510156124dd57604051632f789f4960e21b815260040160405180910390fd5b6000808360000160039054906101000a90046001600160a01b03166001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b15801561253057600080fd5b505afa158015612544573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612568919061595d565b5091509150808b8b88818110612580576125806159cb565b9050602002013511156125cb578a8a8781811061259f5761259f6159cb565b9050602002013581604051630b72c59d60e21b8152600401610ee0929190918252602082015260400190565b828b8b888181106125de576125de6159cb565b905060200201356125ef91906159e1565b6125f990886159f8565b96508282101561264157847fdd2523ca96a639ba7e17420698937f71eddd8af012ccb36ff5c8fe96141acae961262f84866159e1565b60405190815260200160405180910390a25b8a8a87818110612653576126536159cb565b905060200201358460040181905550856001019550505050505061246c565b509695505050505050565b7f779e5c23cb7a5bcb9bfe1e9a5165a00057f12bcdfd13e374540fdf1a1cd911376126a881336137a9565b8382146126d25760405163098b37e560e31b81526004810185905260248101839052604401610ee0565b60005b8481101561284b5760008484838181106126f1576126f16159cb565b905060200201351115612843576000612721878784818110612715576127156159cb565b9050602002013561380d565b54630100000090046001600160a01b0316905080638d7e401786868581811061274c5761274c6159cb565b905060200201356040518263ffffffff1660e01b815260040161277191815260200190565b600060405180830381600087803b15801561278b57600080fd5b505af192505050801561279c575060015b612841573d8080156127ca576040519150601f19603f3d011682016040523d82523d6000602084013e6127cf565b606091505b5080516127ef57604051638fd297d960e01b815260040160405180910390fd5b878784818110612801576128016159cb565b905060200201357ff74208fedac7280fd11f8de0be14e00423dc5076da8e8ec8ca90e09257fff1b3826040516128379190615bf2565b60405180910390a2505b505b6001016126d5565b505050505050565b6060806060600080600080612866613dc9565b80519193509150801580612878575082155b156128ba5750506040805160008082526020820181815282840182815260608401909452919850909650909450925068056bc75e2d631000009150612bc79050565b68056bc75e2d631000009350806001600160401b038111156128de576128de614ea7565b604051908082528060200260200182016040528015612907578160200160208202803683370190505b509650806001600160401b0381111561292257612922614ea7565b60405190808252806020026020018201604052801561294b578160200160208202803683370190505b509750806001600160401b0381111561296657612966614ea7565b60405190808252806020026020018201604052801561298f578160200160208202803683370190505b5095506000808060005b84811015612b945760008682815181106129b5576129b56159cb565b602002602001015160c001511115612b8c578581815181106129d9576129d96159cb565b60200260200101516020015162ffffff168b85815181106129fc576129fc6159cb565b6020026020010181815250508688878381518110612a1c57612a1c6159cb565b602002602001015160c00151612a329190615ae7565b612a3c91906159b7565b9250858181518110612a5057612a506159cb565b6020026020010151600001518c8581518110612a6e57612a6e6159cb565b60200260200101906001600160a01b031690816001600160a01b031681525050612710868281518110612aa357612aa36159cb565b60200260200101516040015161ffff1684612abe9190615ae7565b612ac891906159b7565b91506002868281518110612ade57612ade6159cb565b602002602001015160a001516002811115612afb57612afb615538565b14612b3457818a8581518110612b1357612b136159cb565b60200260200101906001600160601b031690816001600160601b0316815250505b81612710878381518110612b4a57612b4a6159cb565b60200260200101516060015161ffff1685612b659190615ae7565b612b6f91906159b7565b612b799190615c05565b612b83908a615c05565b98506001909301925b600101612999565b5086886001600160601b03161115612bae57612bae615bdc565b83831015612bc057828a52828b528289525b5050505050505b9091929394565b612bd6614cf4565b612bdf8261380d565b6040805161014081018252825462ffffff81168252630100000081046001600160a01b03166020830152600160b81b810461ffff90811693830193909352600160c81b810483166060830152600160d81b81049092166080820152600160e81b90910460ff1660a082015260018201805491929160c084019190612c6290615a97565b80601f0160208091040260200160405190810160405280929190818152602001828054612c8e90615a97565b8015612cdb5780601f10612cb057610100808354040283529160200191612cdb565b820191906000526020600020905b815481529060010190602001808311612cbe57829003601f168201915b505050918352505060028201546001600160401b031660208201526003820154604082015260049091015460609091015292915050565b60006060612d1f83613820565b509094909350915050565b600080516020615f21833981519152612d4381336137a9565b6000612d4e8761380d565b54630100000090046001600160a01b03169050612d6d86868686613e97565b604051634d8060a360e11b81526001600160a01b03821690639b00c14690612d9f908990899089908990600401615c27565b600060405180830381600087803b158015612db957600080fd5b505af11580156122f6573d6000803e3d6000fd5b6000818152600080516020615ee183398151915260205260408120610d3c90613f3d565b600080516020615f21833981519152612e0a81336137a9565b6000612e158761380d565b54630100000090046001600160a01b03169050612e3486868686613e97565b604051629b3d1960e81b81526001600160a01b03821690639b3d190090612d9f908990899089908990600401615c27565b600080516020615f41833981519152612e7e81336137a9565b6000612e898461380d565b9050826002811115612e9d57612e9d615538565b8154600160e81b900460ff166002811115612eba57612eba615538565b6002811115612ecb57612ecb615538565b1415612eea57604051635ca16fa760e11b815260040160405180910390fd5b612ef48184613b0f565b50505050565b612f038261110a565b612f0d81336137a9565b6111498383613a6d565b7f9a2f67efb89489040f2c48c3b2c38f719fba1276678d2ced3bd9049fb5edc6b2612f4281336137a9565b6000612f4d8361380d565b905060018154600160e81b900460ff166002811115612f6e57612f6e615538565b6002811115612f7f57612f7f615538565b14612f9d576040516316c1da1560e21b815260040160405180910390fd5b611149816000613b0f565b600080516020615f21833981519152612fc181336137a9565b6000612fcb61169e565b905060005b81811015611149576000612fe382613a9c565b905060008160000160039054906101000a90046001600160a01b031690506000816001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b15801561303c57600080fd5b505afa158015613050573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613074919061595d565b50509050826004015481141561316757816001600160a01b031663e864299e6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156130bf57600080fd5b505af19250505080156130d0575060015b613167573d8080156130fe576040519150601f19603f3d011682016040523d82523d6000602084013e613103565b606091505b50805161312357604051638fd297d960e01b815260040160405180910390fd5b835460405162ffffff909116907fe74bf895f0c3a2d6c74c40cbb362fdd9640035fc4226c72e3843809ad2a9d2b59061315d908490615bf2565b60405180910390a2505b836001019350505050612fd0565b600080516020615f4183398151915261318e81336137a9565b60006131998561380d565b5460405163a2e080f160e01b8152600481018690526024810185905263010000009091046001600160a01b03169150819063a2e080f190604401600060405180830381600087803b1580156131ed57600080fd5b505af1158015610fc1573d6000803e3d6000fd5b60006001611a8b565b7fe7c742a54cd11fc9749a47ab34bdcd7327820908e8d0d48b4a5c7f17b029409861323581336137a9565b61325e7fabeb05279af36da5d476d7f950157cd2ea98a4166fa68a6bc97ce3a22fbb93c0839055565b600061326861169e565b905060005b8181101561339357600061328082613a9c565b90508160010191508060000160039054906101000a90046001600160a01b03166001600160a01b03166390c09bdb6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156132da57600080fd5b505af19250505080156132eb575060015b61338d573d808015613319576040519150601f19603f3d011682016040523d82523d6000602084013e61331e565b606091505b50805161333e57604051638fd297d960e01b815260040160405180910390fd5b613349826001613b0f565b815460405162ffffff909116907f0d64b11929aa111ca874dd00b5b0cc2d82b741be924ec9e3691e67c71552f62390613383908490615bf2565b60405180910390a2505b5061326d565b50604080518481523360208201527f82e72df77173eab89b00556d791a407a78f4605c5c2f0694967c8c429dd43c7c91016118ab565b60008060008060006133d96136dc565b92506001600160601b031692506001600160601b031692506133fb8382613c30565b94506134078282613c30565b93505050509091565b6060600061341d84613787565b905082516001600160401b0381111561343857613438614ea7565b60405190808252806020026020018201604052801561347157816020015b61345e614ddd565b8152602001906001900390816134565790505b50915060005b835181101561359c57604051806060016040528085838151811061349d5761349d6159cb565b60200260200101518152602001836001600160a01b0316635e2fb9088785815181106134cb576134cb6159cb565b60200260200101516040518263ffffffff1660e01b81526004016134f191815260200190565b60206040518083038186803b15801561350957600080fd5b505afa15801561351d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135419190615c59565b1515815260200161356b8787858151811061355e5761355e6159cb565b6020026020010151612304565b815250838281518110613580576135806159cb565b60200260200101819052508061359590615acc565b9050613477565b505092915050565b606060006135b061169e565b9050806001600160401b038111156135ca576135ca614ea7565b6040519080825280602002602001820160405280156135f3578160200160208202803683370190505b50915060005b81811015611a825761360a81613a9c565b54835162ffffff90911690849083908110613627576136276159cb565b60209081029190910101526001016135f9565b6060600061364785613787565b604051634febc81b60e01b815260048101869052602481018590529091506000906001600160a01b03831690634febc81b9060440160006040518083038186803b15801561369457600080fd5b505afa1580156136a8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136d09190810190615c76565b90506111008682613410565b6000806000606060006136ed612853565b9650909450925060009150505b825181101561373a57828181518110613715576137156159cb565b6020026020010151866137289190615c05565b955061373381615acc565b90506136fa565b506137458582615cfb565b93505050909192565b9055565b60006001600160e01b03198216637965db0b60e01b1480610d3c57506301ffc9a760e01b6001600160e01b0319831614610d3c565b60006137928261380d565b54630100000090046001600160a01b031692915050565b6137b38282611f50565b611260576137cb816001600160a01b03166014613f47565b6137d6836020613f47565b6040516020016137e7929190615d23565b60408051601f198184030181529082905262461bcd60e51b8252610ee091600401615bf2565b6000610d3c61381b836139e5565b613a9c565b6000606080600061382f613dc9565b8051909350909150806001600160401b0381111561384f5761384f614ea7565b604051908082528060200260200182016040528015613878578160200160208202803683370190505b50935080156139dc5761388b86836159f8565b91506000816001600160401b038111156138a7576138a7614ea7565b6040519080825280602002602001820160405280156138d0578160200160208202803683370190505b5090506000805b838110156139cb578581815181106138f1576138f16159cb565b602002602001015160c0015187828151811061390f5761390f6159cb565b60200260200101818152505061271085878381518110613931576139316159cb565b60200260200101516080015161ffff1661394b9190615ae7565b61395591906159b7565b91506139a68287838151811061396d5761396d6159cb565b602002602001015160e0015188848151811061398b5761398b6159cb565b602002602001015160c001516139a191906159f8565b6140e2565b8382815181106139b8576139b86159cb565b60209081029190910101526001016138d7565b506139d786838a6140f1565b965050505b50509193909250565b60008181527f9b48f5b32acb95b982effe269feac267eead113c4b5af14ffeb9aadac18a6e9c6020819052604082205480613a3357604051636a0eb14160e11b815260040160405180910390fd5b6111de6001826159e1565b613a488282614136565b6000828152600080516020615ee18339815191526020526040902061114990826141ac565b613a7782826141c1565b6000828152600080516020615ee1833981519152602052604090206111499082614235565b60009081527f1d2f69fc9b5fe89d7414bf039e8d897c4c487c7603d80de6bcdd2868466f94766020526040902090565b7f9b48f5b32acb95b982effe269feac267eead113c4b5af14ffeb9aadac18a6e9c613af88260016159f8565b600093845260209190915260409092209190915550565b8154600090600160e81b900460ff166002811115613b2f57613b2f615538565b9050816002811115613b4357613b43615538565b816002811115613b5557613b55615538565b1461114957816002811115613b6c57613b6c615538565b835460ff91909116600160e81b0260ff60e81b1982168117855560405162ffffff9182169190921617907ffd6f15fb2b48a21a60fe3d44d3c3a0433ca01e121b5124a63ec45c30ad925a1790613bc59085903390615d92565b60405180910390a2505050565b613bda611d69565b15613bf85760405163184e52a160e21b815260040160405180910390fd5b613c018161424a565b50565b6112608282613a3e565b6000611f4983836142a9565b6000818311613c295781611f49565b5090919050565b600081613c3f61271085615ae7565b611f4991906159b7565b613c54846030615ae7565b825114613c8a578151613c68856030615ae7565b6040516346b38e7960e11b815260048101929092526024820152604401610ee0565b613c95846060615ae7565b815114613ccb578051613ca9856060615ae7565b604051633c11c1f760e21b815260048101929092526024820152604401610ee0565b6000613cd760306142d3565b90506000613ce560606142d3565b905060005b868110156122fb57613d0b8584613d02603085615ae7565b600060306142ec565b613d248483613d1b606085615ae7565b600060606142ec565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663228951186a1a784379d99db420000000858986613d6d8c8a8a614373565b6040518663ffffffff1660e01b8152600401613d8c9493929190615db8565b6000604051808303818588803b158015613da557600080fd5b505af1158015613db9573d6000803e3d6000fd5b5050505050806001019050613cea565b600060606000613dd761169e565b9050806001600160401b03811115613df157613df1614ea7565b604051908082528060200260200182016040528015613e2a57816020015b613e17614dfc565b815260200190600190039081613e0f5790505b50915060005b81811015613e9157613e41816146ef565b838281518110613e5357613e536159cb565b6020026020010181905250828181518110613e7057613e706159cb565b602002602001015160c0015184613e8791906159f8565b9350600101613e30565b50509091565b613ea2600884615e03565b151580613eb85750613eb5601082615e03565b15155b15613ed9576040516363209a7d60e11b815260036004820152602401610ee0565b6000613ee66008856159b7565b905080613ef46010846159b7565b14613f15576040516363209a7d60e11b815260026004820152602401610ee0565b80613f36576040516363209a7d60e11b815260016004820152602401610ee0565b5050505050565b6000610d3c825490565b60606000613f56836002615ae7565b613f619060026159f8565b6001600160401b03811115613f7857613f78614ea7565b6040519080825280601f01601f191660200182016040528015613fa2576020820181803683370190505b509050600360fc1b81600081518110613fbd57613fbd6159cb565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110613fec57613fec6159cb565b60200101906001600160f81b031916908160001a9053506000614010846002615ae7565b61401b9060016159f8565b90505b6001811115614093576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061404f5761404f6159cb565b1a60f81b828281518110614065576140656159cb565b60200101906001600160f81b031916908160001a90535060049490941c9361408c81615e17565b905061401e565b508315611f495760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610ee0565b6000818310613c295781611f49565b6000805b8282101561412e57614111858561410c85876159e1565b61486e565b90508061411d5761412e565b61412781836159f8565b91506140f5565b509392505050565b6141408282611f50565b611260576000828152600080516020615f01833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6000611f49836001600160a01b038416614aac565b6141cb8282611f50565b15611260576000828152600080516020615f01833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611f49836001600160a01b038416614afb565b6142737f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6829055565b6040518181527ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9060200160405180910390a150565b60008260000182815481106142c0576142c06159cb565b9060005260206000200154905092915050565b60408051828152603f92810192909201601f1916905290565b84516142f882856159f8565b111580156143105750835161430d82846159f8565b11155b61435c5760405162461bcd60e51b815260206004820152601960248201527f42595445535f41525241595f4f55545f4f465f424f554e4453000000000000006044820152606401610ee0565b6020838601810190838601016122fb828285614bee565b60008061438060406142d3565b90506000614398614393604060606159e1565b6142d3565b90506143a9848360008060406142ec565b6143c28482604060006143bd8260606159e1565b6142ec565b6000600286600060801b6040516020016143dd929190615e2e565b60408051601f19818403018152908290526143f791615e66565b602060405180830381855afa158015614414573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061443791906158da565b905060006002808560405160200161444f9190615e66565b60408051601f198184030181529082905261446991615e66565b602060405180830381855afa158015614486573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906144a991906158da565b6040516002906144c0908790600090602001615e82565b60408051601f19818403018152908290526144da91615e66565b602060405180830381855afa1580156144f7573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061451a91906158da565b60408051602081019390935282015260600160408051601f198184030181529082905261454691615e66565b602060405180830381855afa158015614563573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061458691906158da565b9050600280838a60405160200161459e929190615ea4565b60408051601f19818403018152908290526145b891615e66565b602060405180830381855afa1580156145d5573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906145f891906158da565b6040805164d098d4af7160c81b60208201526000602882015290810184905260029060600160408051601f198184030181529082905261463791615e66565b602060405180830381855afa158015614654573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061467791906158da565b60408051602081019390935282015260600160408051601f19818403018152908290526146a391615e66565b602060405180830381855afa1580156146c0573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906146e391906158da565b98975050505050505050565b6146f7614dfc565b600061470283613a9c565b80546001600160a01b036301000000820416845262ffffff8116602085015261ffff600160b81b820481166040860152600160c81b820481166060860152600160d81b820416608085015290915060ff600160e81b90910416600281111561476c5761476c615538565b8260a00190600281111561478257614782615538565b9081600281111561479557614795615538565b81525050600080600084600001516001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b1580156147db57600080fd5b505afa1580156147ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614813919061595d565b9194509250905060008560a00151600281111561483257614832615538565b1461483e576000614840565b805b60e08601526004840154614855908490613c1a565b61485f90836159e1565b60c08601525092949350505050565b825160009060001982846148885760009350505050611f49565b60005b875181101561495a578681815181106148a6576148a66159cb565b60200260200101518882815181106148c0576148c06159cb565b6020026020010151106148d25761494a565b8781815181106148e4576148e46159cb565b602002602001015183111561491b578093506001915087818151811061490c5761490c6159cb565b6020026020010151925061494a565b87818151811061492d5761492d6159cb565b602002602001015183141561494a576149476001836159f8565b91505b61495381615acc565b905061488b565b508061496c5760009350505050611f49565b60001960005b8851811015614a2b5787818151811061498d5761498d6159cb565b60200260200101518982815181106149a7576149a76159cb565b6020026020010151106149b957614a1b565b838982815181106149cc576149cc6159cb565b60200260200101511180156149f95750818982815181106149ef576149ef6159cb565b6020026020010151105b15614a1b57888181518110614a1057614a106159cb565b602002602001015191505b614a2481615acc565b9050614972565b50614a7560018311614a3d5786614a47565b614a478784614c39565b84614a6b848b8981518110614a5e57614a5e6159cb565b60200260200101516140e2565b6139a191906159e1565b945084888581518110614a8a57614a8a6159cb565b60200260200101818151614a9e91906159f8565b905250505050509392505050565b6000818152600183016020526040812054614af357508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610d3c565b506000610d3c565b60008181526001830160205260408120548015614be4576000614b1f6001836159e1565b8554909150600090614b33906001906159e1565b9050818114614b98576000866000018281548110614b5357614b536159cb565b9060005260206000200154905080876000018481548110614b7657614b766159cb565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614ba957614ba9615eca565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610d3c565b6000915050610d3c565b5b601f811115614c0f578251825260209283019290910190601f1901614bef565b80156111495782518251600019600160086020869003021b01908116901991909116178252505050565b60008215614c675781614c4d6001856159e1565b614c5791906159b7565b614c629060016159f8565b611f49565b50600092915050565b828054614c7c90615a97565b90600052602060002090601f016020900481019282614c9e5760008555614ce4565b82601f10614cb75782800160ff19823516178555614ce4565b82800160010185558215614ce4579182015b82811115614ce4578235825591602001919060010190614cc9565b50614cf0929150614e41565b5090565b604080516101408101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c082015260e08101829052610100810182905261012081019190915290565b60405180608001604052806000815260200160008152602001614d68614cf4565b8152602001614d9160405180606001604052806000815260200160008152602001600081525090565b905290565b604051806101000160405280600015158152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160608101825260008082526020820152908101614d91614d96565b604080516101008101825260008082526020820181905291810182905260608101829052608081018290529060a0820190815260200160008152602001600081525090565b5b80821115614cf05760008155600101614e42565b600060208284031215614e6857600080fd5b81356001600160e01b031981168114611f4957600080fd5b600060208284031215614e9257600080fd5b5035919050565b8015158114613c0157600080fd5b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b0381118282101715614edf57614edf614ea7565b60405290565b604051601f8201601f191681016001600160401b0381118282101715614f0d57614f0d614ea7565b604052919050565b600080600080848603610120811215614f2d57600080fd5b85359450602086013593506040860135614f4681614e99565b925060c0605f1982011215614f5a57600080fd5b50614f63614ebd565b606086013581526080860135602082015260a0860135604082015260c0860135606082015260e0860135608082015261010086013560a08201528091505092959194509250565b81518152602080830151908201526040808301519082015260608101610d3c565b60008060408385031215614fde57600080fd5b50508035926020909101359150565b80356001600160a01b038116811461500457600080fd5b919050565b6000806040838503121561501c57600080fd5b8235915061502c60208401614fed565b90509250929050565b8051151582526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301525050565b602080825282518282018190526000919060409081850190868401855b828110156150de57815180518552868101511515878601528501516150c986860182615035565b506101409390930192908501906001016150a2565b5091979650505050505050565b60008083601f8401126150fd57600080fd5b5081356001600160401b0381111561511457600080fd5b60208301915083602082850101111561512c57600080fd5b9250929050565b60008060008060008060a0878903121561514c57600080fd5b86356001600160401b0381111561516257600080fd5b61516e89828a016150eb565b9097509550615181905060208801614fed565b93506040870135925060608701359150608087013590509295509295509295565b60005b838110156151bd5781810151838201526020016151a5565b83811115612ef45750506000910152565b600081518084526151e68160208601602086016151a2565b601f01601f19169290920160200192915050565b805162ffffff1682526000610140602083015161522260208601826001600160a01b03169052565b506040830151615238604086018261ffff169052565b50606083015161524e606086018261ffff169052565b506080830151615264608086018261ffff169052565b5060a083015161527960a086018260ff169052565b5060c08301518160c0860152615291828601826151ce565b91505060e08301516152ae60e08601826001600160401b03169052565b5061010083810151908501526101209283015192909301919091525090565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561536457603f19898403018552815160c081518552888201518986015287820151818987015261532a828701826151fa565b60609384015180518886015260208101516080890152604081015160a08901529390925090505095880195935050908601906001016152f4565b509098975050505050505050565b60008060006060848603121561538757600080fd5b61539084614fed565b925061539e60208501614fed565b9150604084013590509250925092565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561540357603f198886030184526153f18583516151fa565b945092850192908501906001016153d5565b5092979650505050505050565b6000806000806080858703121561542657600080fd5b8435935060208501359250604085013561543f81614e99565b9396929550929360600135925050565b60006001600160401b0382111561546857615468614ea7565b5060051b60200190565b600082601f83011261548357600080fd5b813560206154986154938361544f565b614ee5565b82815260059290921b840181019181810190868411156154b757600080fd5b8286015b8481101561267257803583529183019183016154bb565b6000602082840312156154e457600080fd5b81356001600160401b038111156154fa57600080fd5b6111de84828501615472565b6000806000806080858703121561551c57600080fd5b5050823594602084013594506040840135936060013592509050565b634e487b7160e01b600052602160045260246000fd5b6003811061556c57634e487b7160e01b600052602160045260246000fd5b9052565b60208101610d3c828461554e565b6000806000806060858703121561559457600080fd5b843593506020850135925060408501356001600160401b038111156155b857600080fd5b6155c4878288016150eb565b95989497509550505050565b6101008101610d3c8284615035565b60008083601f8401126155f157600080fd5b5081356001600160401b0381111561560857600080fd5b6020830191508360208260051b850101111561512c57600080fd5b6000806000806040858703121561563957600080fd5b84356001600160401b038082111561565057600080fd5b61565c888389016155df565b9096509450602087013591508082111561567557600080fd5b506155c4878288016155df565b600081518084526020808501945080840160005b838110156156b257815187529582019590820190600101615696565b509495945050505050565b60a0808252865190820181905260009060209060c0840190828a01845b828110156156ff5781516001600160a01b0316845292840192908401906001016156da565b505050838103828501526157138189615682565b8481036040860152875180825283890192509083019060005b818110156157515783516001600160601b03168352928401929184019160010161572c565b50506001600160601b0387166060860152925061576c915050565b8260808301529695505050505050565b602081526000611f4960208301846151fa565b8281526040602082015260006111de6040830184615682565b6000806000806000606086880312156157c057600080fd5b8535945060208601356001600160401b03808211156157de57600080fd5b6157ea89838a016150eb565b9096509450604088013591508082111561580357600080fd5b50615810888289016150eb565b969995985093965092949392505050565b6000806040838503121561583457600080fd5b8235915060208301356003811061584a57600080fd5b809150509250929050565b60008060006060848603121561586a57600080fd5b505081359360208301359350604090920135919050565b6000806040838503121561589457600080fd5b8235915060208301356001600160401b038111156158b157600080fd5b6158bd85828601615472565b9150509250929050565b602081526000611f496020830184615682565b6000602082840312156158ec57600080fd5b5051919050565b600080600080600080600080610100898b03121561591057600080fd5b885161591b81614e99565b809850506020890151965060408901519550606089015194506080890151935060a0890151925060c0890151915060e089015190509295985092959890939650565b60008060006060848603121561597257600080fd5b8351925060208401519150604084015190509250925092565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000826159c6576159c661598b565b500490565b634e487b7160e01b600052603260045260246000fd5b6000828210156159f3576159f36159a1565b500390565b60008219821115615a0b57615a0b6159a1565b500190565b600062ffffff808316818516808303821115615a2e57615a2e6159a1565b01949350505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600060018060a01b03808716835260606020840152615a83606084018688615a37565b915080841660408401525095945050505050565b600181811c90821680615aab57607f821691505b60208210811415611d6357634e487b7160e01b600052602260045260246000fd5b6000600019821415615ae057615ae06159a1565b5060010190565b6000816000190483118215151615615b0157615b016159a1565b500290565b83815260406020820152600061203e604083018486615a37565b600082601f830112615b3157600080fd5b81516001600160401b03811115615b4a57615b4a614ea7565b615b5d601f8201601f1916602001614ee5565b818152846020838601011115615b7257600080fd5b6111de8260208301602087016151a2565b60008060408385031215615b9657600080fd5b82516001600160401b0380821115615bad57600080fd5b615bb986838701615b20565b93506020850151915080821115615bcf57600080fd5b506158bd85828601615b20565b634e487b7160e01b600052600160045260246000fd5b602081526000611f4960208301846151ce565b60006001600160601b03808316818516808303821115615a2e57615a2e6159a1565b604081526000615c3b604083018688615a37565b8281036020840152615c4e818587615a37565b979650505050505050565b600060208284031215615c6b57600080fd5b8151611f4981614e99565b60006020808385031215615c8957600080fd5b82516001600160401b03811115615c9f57600080fd5b8301601f81018513615cb057600080fd5b8051615cbe6154938261544f565b81815260059190911b82018301908381019087831115615cdd57600080fd5b928401925b82841015615c4e57835182529284019290840190615ce2565b60006001600160601b0383811690831681811015615d1b57615d1b6159a1565b039392505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351615d558160178501602088016151a2565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615d868160288401602088016151a2565b01602801949350505050565b60408101615da0828561554e565b6001600160a01b039290921660209190910152919050565b608081526000615dcb60808301876151ce565b8281036020840152615ddd81876151ce565b90508281036040840152615df181866151ce565b91505082606083015295945050505050565b600082615e1257615e1261598b565b500690565b600081615e2657615e266159a1565b506000190190565b60008351615e408184602088016151a2565b6fffffffffffffffffffffffffffffffff19939093169190920190815260100192915050565b60008251615e788184602087016151a2565b9190910192915050565b60008351615e948184602088016151a2565b9190910191825250602001919050565b82815260008251615ebc8160208501602087016151a2565b919091016020019392505050565b634e487b7160e01b600052603160045260246000fdfe8f8c450dae5029cd48cd91dd9db65da48fb742893edfc7941250f6721d93cbbe9a627a5d4aa7c17f87ff26e3fe9a42c2b6c559e8b41a42282d0ecebb17c0e4d3c23292b191d95d2a7dd94fc6436eb44338fda9e1307d9394fd27c28157c1b33c3105bcbf19d4417b73ae0e58d508a65ecf75665e46c2622d8521732de6080c48a2646970667358221220f4c91cf037a6c582ea19427dce3b228670f62711b8daea8e96ce318f5d88724c64736f6c634300080900330000000000000000000000003693693693693693693693693693693693693693
Deployed ByteCode
0x6080604052600436106103d25760003560e01c80639010d07c116101fd578063c82b1bb111610118578063e016e6f7116100ab578063efcdcc0e1161007a578063efcdcc0e14610c4a578063f07ff28a14610c7a578063f2aebb6514610c9a578063f8bb6d4214610cbc578063fa5093eb14610cdc57600080fd5b8063e016e6f714610bc8578063e1b92a5c14610bea578063e24ce9f114610c0a578063e97ee8cc14610c2a57600080fd5b8063d0a2b1b8116100e7578063d0a2b1b814610b53578063d547741f14610b73578063d861c58414610b93578063db3c7ba714610bb357600080fd5b8063c82b1bb114610ac5578063c8ac498014610af3578063ca15c87314610b13578063cb589b9a14610b3357600080fd5b8063a7357c8c11610190578063af1240971161015f578063af12409714610a30578063ba21ccae14610a50578063bc1bb19014610a76578063c445ea7514610aa357600080fd5b8063a7357c8c1461099d578063aa0b7db7146109d0578063aa5a1b9d146109e3578063abd44a2414610a1057600080fd5b80639fbb7bae116101cc5780639fbb7bae146108e55780639fc5a6ed1461090d578063a217fddf1461093a578063a734329c1461094f57600080fd5b80639010d07c1461087057806391d148541461089057806396b5d81c146108b05780639b75b4ef146108d057600080fd5b806356396715116102ed5780636b96736b116102805780638525e3a11161024f5780638525e3a1146107e75780638801da79146108075780638aa104351461083b5780638dc70c571461085057600080fd5b80636b96736b146107425780637443f523146107765780637a74884d146107965780637c8da51c146107ca57600080fd5b80636183214d116102bc5780636183214d146106b35780636608b11b146106d55780636a516b47146106f55780636ada55b91461072257600080fd5b8063563967151461063c57806357993b85146106515780635bf55e40146106735780636133f9851461069357600080fd5b8063271662ec116103655780633e54ee5b116103345780633e54ee5b146105d2578063473e0433146105f25780634a7583b6146106125780634b3a1cb71461062757600080fd5b8063271662ec1461054f5780632f2ff15d146105655780633240a3221461058557806336568abe146105b257600080fd5b80631565d2f2116103a15780631565d2f2146104a757806319c64b79146104db5780631d1b9d3c146104fb578063248a9ca31461052f57600080fd5b806301ffc9a7146103f55780630519fbbf1461042a578063072859c71461045857806307e203ac1461047a57600080fd5b366103f0576040516309fb455960e41b815260040160405180910390fd5b600080fd5b34801561040157600080fd5b50610415610410366004614e56565b610d17565b60405190151581526020015b60405180910390f35b34801561043657600080fd5b5061044a610445366004614e80565b610d42565b604051908152602001610421565b34801561046457600080fd5b50610478610473366004614f15565b610dbd565b005b34801561048657600080fd5b5061049a610495366004614e80565b610fcc565b6040516104219190614faa565b3480156104b357600080fd5b5061044a7f55180e25fcacf9af017d35d497765476319b23896daa1f9bc2b38fa80b36a16381565b3480156104e757600080fd5b5061044a6104f6366004614fcb565b61108b565b34801561050757600080fd5b5061044a7f779e5c23cb7a5bcb9bfe1e9a5165a00057f12bcdfd13e374540fdf1a1cd9113781565b34801561053b57600080fd5b5061044a61054a366004614e80565b61110a565b34801561055b57600080fd5b5061044a61271081565b34801561057157600080fd5b50610478610580366004615009565b61112c565b34801561059157600080fd5b506105a56105a0366004614e80565b61114e565b6040516104219190615085565b3480156105be57600080fd5b506104786105cd366004615009565b6111e6565b3480156105de57600080fd5b506104786105ed366004615133565b611264565b3480156105fe57600080fd5b5061044a61060d366004614e80565b611687565b34801561061e57600080fd5b5061044a61169e565b34801561063357600080fd5b5061044a602081565b34801561064857600080fd5b5061044a6116cd565b34801561065d57600080fd5b506106666116f7565b60405161042191906152cd565b34801561067f57600080fd5b5061047861068e366004614e80565b611704565b34801561069f57600080fd5b506104786106ae366004615372565b611794565b3480156106bf57600080fd5b506106c86118b8565b60405161042191906153ae565b3480156106e157600080fd5b506104156106f0366004614e80565b611a87565b34801561070157600080fd5b5061070a611aac565b6040516001600160a01b039091168152602001610421565b34801561072e57600080fd5b5061041561073d366004614e80565b611ad6565b34801561074e57600080fd5b5061070a7f000000000000000000000000369369369369369369369369369369369369369381565b34801561078257600080fd5b50610478610791366004615410565b611adf565b3480156107a257600080fd5b5061044a7fe7c742a54cd11fc9749a47ab34bdcd7327820908e8d0d48b4a5c7f17b029409881565b3480156107d657600080fd5b5061044a68056bc75e2d6310000081565b3480156107f357600080fd5b506106666108023660046154d2565b611b7f565b34801561081357600080fd5b5061044a7f9a2f67efb89489040f2c48c3b2c38f719fba1276678d2ced3bd9049fb5edc6b281565b34801561084757600080fd5b5061044a611d69565b34801561085c57600080fd5b5061047861086b366004615506565b611d93565b34801561087c57600080fd5b5061070a61088b366004614fcb565b611f24565b34801561089c57600080fd5b506104156108ab366004615009565b611f50565b3480156108bc57600080fd5b5061044a6108cb366004614e80565b611f88565b3480156108dc57600080fd5b5061044a601f81565b3480156108f157600080fd5b506108fa612047565b60405161ffff9091168152602001610421565b34801561091957600080fd5b5061092d610928366004614e80565b612075565b6040516104219190615570565b34801561094657600080fd5b5061044a600081565b34801561095b57600080fd5b5061041561096a366004614e80565b60009081527f9b48f5b32acb95b982effe269feac267eead113c4b5af14ffeb9aadac18a6e9c6020526040902054151590565b3480156109a957600080fd5b5061044a7eb1e70095ba5bacc3202c3db9faf1f7873186f0ed7b6c84e80c0018dcc6e38e81565b6104786109de36600461557e565b61209c565b3480156109ef57600080fd5b50610a036109fe366004614fcb565b612304565b60405161042191906155d0565b348015610a1c57600080fd5b5061044a610a2b366004615623565b612423565b348015610a3c57600080fd5b50610478610a4b366004615623565b61267d565b348015610a5c57600080fd5b50610a65612853565b6040516104219594939291906156bd565b348015610a8257600080fd5b50610a96610a91366004614e80565b612bce565b604051610421919061577c565b348015610aaf57600080fd5b5061044a600080516020615f2183398151915281565b348015610ad157600080fd5b50610ae5610ae0366004614e80565b612d12565b60405161042192919061578f565b348015610aff57600080fd5b50610478610b0e3660046157a8565b612d2a565b348015610b1f57600080fd5b5061044a610b2e366004614e80565b612dcd565b348015610b3f57600080fd5b50610478610b4e3660046157a8565b612df1565b348015610b5f57600080fd5b50610478610b6e366004615821565b612e65565b348015610b7f57600080fd5b50610478610b8e366004615009565b612efa565b348015610b9f57600080fd5b50610478610bae366004614e80565b612f17565b348015610bbf57600080fd5b50610478612fa8565b348015610bd457600080fd5b5061044a600080516020615f4183398151915281565b348015610bf657600080fd5b50610478610c05366004615855565b613175565b348015610c1657600080fd5b50610415610c25366004614e80565b613201565b348015610c3657600080fd5b50610478610c45366004614e80565b61320a565b348015610c5657600080fd5b50610c5f6133c9565b6040805161ffff938416815292909116602083015201610421565b348015610c8657600080fd5b506105a5610c95366004615881565b613410565b348015610ca657600080fd5b50610caf6135a4565b60405161042191906158c7565b348015610cc857600080fd5b506105a5610cd7366004615855565b61363a565b348015610ce857600080fd5b50610cf16136dc565b604080516001600160601b03948516815293909216602084015290820152606001610421565b60006001600160e01b03198216635a05180f60e01b1480610d3c5750610d3c82613752565b92915050565b6000610d4d82613787565b6001600160a01b031663d087d2886040518163ffffffff1660e01b815260040160206040518083038186803b158015610d8557600080fd5b505afa158015610d99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3c91906158da565b7f55180e25fcacf9af017d35d497765476319b23896daa1f9bc2b38fa80b36a163610de881336137a9565b6000610df38661380d565b8054604051632cc1db0f60e21b815260048101889052919250630100000090046001600160a01b0316906000908190839063b3076c3c906024016101006040518083038186803b158015610e4657600080fd5b505afa158015610e5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7e91906158f3565b5050955050509350505083600401548660000151141580610ea3575080866020015114155b80610eb2575081866040015114155b15610ee95760048481015460405163e882688560e01b81529182015260248101829052604481018390526064015b60405180910390fd5b6060860151600480860191909155608087015160a088015160405163f2e2ca6360e01b81529283018b9052602483019190915260448201526001600160a01b0384169063f2e2ca6390606401600060405180830381600087803b158015610f4f57600080fd5b505af1158015610f63573d6000803e3d6000fd5b505050508615610fc157826001600160a01b031663e864299e6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610fa857600080fd5b505af1158015610fbc573d6000803e3d6000fd5b505050505b505050505050505050565b610ff060405180606001604052806000815260200160008152602001600081525090565b6000610ffb83612bce565b9050600081602001519050806001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b15801561103f57600080fd5b505afa158015611053573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611077919061595d565b604086015260208501528352509092915050565b600080806110ac6110a76a1a784379d99db420000000866159b7565b613820565b925092505060006110bc866139e5565b90508181815181106110d0576110d06159cb565b602002602001015160c001518382815181106110ee576110ee6159cb565b602002602001015161110091906159e1565b9695505050505050565b6000908152600080516020615f01833981519152602052604090206001015490565b6111358261110a565b61113f81336137a9565b6111498383613a3e565b505050565b6060600061115b83613787565b90506000816001600160a01b031663a70c70e46040518163ffffffff1660e01b815260040160206040518083038186803b15801561119857600080fd5b505afa1580156111ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d091906158da565b90506111de8460008361363a565b949350505050565b6001600160a01b03811633146112565760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610ee0565b6112608282613a6d565b5050565b600080516020615f4183398151915261127d81336137a9565b6127108411156112bf57604051630cb4392560e31b815260206004820152600c60248201526b5f746172676574536861726560a01b6044820152606401610ee0565b6127106112cc83856159f8565b111561131b57604051630cb4392560e31b815260206004820181905260248201527f5f7374616b696e674d6f64756c65466565202b205f74726561737572794665656044820152606401610ee0565b6001600160a01b03851661136a5760405163eac0d38960e01b81526020600482015260156024820152745f7374616b696e674d6f64756c654164647265737360581b6044820152606401610ee0565b8515806113775750601f86115b156113955760405163ac18716960e01b815260040160405180910390fd5b600061139f61169e565b9050602081106113c25760405163309eed9960e01b815260040160405180910390fd5b60005b81811015611412576113d681613a9c565b546001600160a01b03888116630100000090920416141561140a5760405163050f969d60e41b815260040160405180910390fd5b6001016113c5565b50600061141e82613a9c565b9050600061144a7ff9a85ae945d8134f58bd2ee028636634dcb9e812798acb5c806bf1951232a2255490565b611455906001615a10565b825462ffffff191662ffffff82161783559050611476600183018b8b614c70565b508154630100000065ffff0000000160b81b03191663010000006001600160a01b038a160261ffff60d81b191617600160d81b61ffff898116919091029190911763ffffffff60b81b1916600160b81b8883160261ffff60c81b191617600160c81b918716919091021760ff60e81b1916825560028201805467ffffffffffffffff1916426001600160401b03161790554360038301556040805160008152905162ffffff8316917f9151b7f88aca05d432bb395647ef52b2ffc454e3c6afb69c95345af6b5a778c0919081900360200190a26115588162ffffff1684613acc565b62ffffff81167ff9a85ae945d8134f58bd2ee028636634dcb9e812798acb5c806bf1951232a225556115b261158e8460016159f8565b7f1b3ef9db2d6f0727a31622833b45264c21051726d23ddb6f73b3b65628cafcc355565b8062ffffff167f43b5213f0e1666cd0b8692a73686164c94deb955a59c65e10dee8bb958e7ce3e898c8c336040516115ed9493929190615a60565b60405180910390a26040805188815233602082015262ffffff8316917f065e5bd8e4145dd99cf69bad5871ad52d094aee07a67fcf2f418c89e49d5f20c910160405180910390a260408051878152602081018790523381830152905162ffffff8316917f303c8ac43d1b1f9b898ddd2915a294efa01e9b07c322d7deeb7db332b66f0410919081900360600190a250505050505050505050565b6000806116938361380d565b600301549392505050565b60006116c87f1b3ef9db2d6f0727a31622833b45264c21051726d23ddb6f73b3b65628cafcc35490565b905090565b60006116c87fabeb05279af36da5d476d7f950157cd2ea98a4166fa68a6bc97ce3a22fbb93c05490565b60606116c86108026135a4565b7eb1e70095ba5bacc3202c3db9faf1f7873186f0ed7b6c84e80c0018dcc6e38e61172e81336137a9565b60006117398361380d565b905060008154600160e81b900460ff16600281111561175a5761175a615538565b600281111561176b5761176b615538565b146117895760405163322e64fb60e11b815260040160405180910390fd5b611149816001613b0f565b6001600160a01b0383166117d45760405163eac0d38960e01b81526020600482015260066024820152652fb0b236b4b760d11b6044820152606401610ee0565b6001600160a01b0382166118135760405163eac0d38960e01b81526020600482015260056024820152645f6c69646f60d81b6044820152606401610ee0565b61181d6001613bd2565b611828600084613c04565b6118517f706b9ed9846c161ad535be9b6345c3a7b2cb929e8d4a7254dee9ba6e6f8e5531839055565b61187a7fabeb05279af36da5d476d7f950157cd2ea98a4166fa68a6bc97ce3a22fbb93c0829055565b604080518281523360208201527f82e72df77173eab89b00556d791a407a78f4605c5c2f0694967c8c429dd43c7c91015b60405180910390a1505050565b606060006118c461169e565b9050806001600160401b038111156118de576118de614ea7565b60405190808252806020026020018201604052801561191757816020015b611904614cf4565b8152602001906001900390816118fc5790505b50915060005b81811015611a825761192e81613a9c565b6040805161014081018252825462ffffff81168252630100000081046001600160a01b03166020830152600160b81b810461ffff90811693830193909352600160c81b810483166060830152600160d81b81049092166080820152600160e81b90910460ff1660a082015260018201805491929160c0840191906119b190615a97565b80601f01602080910402602001604051908101604052809291908181526020018280546119dd90615a97565b8015611a2a5780601f106119ff57610100808354040283529160200191611a2a565b820191906000526020600020905b815481529060010190602001808311611a0d57829003601f168201915b505050918352505060028201546001600160401b03166020820152600382015460408201526004909101546060909101528351849083908110611a6f57611a6f6159cb565b602090810291909101015260010161191d565b505090565b6000805b611a9483612075565b6002811115611aa557611aa5615538565b1492915050565b60006116c87f706b9ed9846c161ad535be9b6345c3a7b2cb929e8d4a7254dee9ba6e6f8e55315490565b60006002611a8b565b600080516020615f41833981519152611af881336137a9565b6000611b038661380d565b546040516354f3d42360e11b81526004810187905285151560248201526044810185905263010000009091046001600160a01b03169150819063a9e7a84690606401600060405180830381600087803b158015611b5f57600080fd5b505af1158015611b73573d6000803e3d6000fd5b50505050505050505050565b606081516001600160401b03811115611b9a57611b9a614ea7565b604051908082528060200260200182016040528015611bd357816020015b611bc0614d47565b815260200190600190039081611bb85790505b50905060005b8251811015611d63576000611c06848381518110611bf957611bf96159cb565b6020026020010151612bce565b90506000816020015190506040518060800160405280826001600160a01b031663a70c70e46040518163ffffffff1660e01b815260040160206040518083038186803b158015611c5557600080fd5b505afa158015611c69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8d91906158da565b8152602001826001600160a01b0316638469cbd36040518163ffffffff1660e01b815260040160206040518083038186803b158015611ccb57600080fd5b505afa158015611cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d0391906158da565b8152602001838152602001611d30878681518110611d2357611d236159cb565b6020026020010151610fcc565b815250848481518110611d4557611d456159cb565b6020026020010181905250505080611d5c90615acc565b9050611bd9565b50919050565b60006116c87f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a65490565b600080516020615f41833981519152611dac81336137a9565b612710841115611dee57604051630cb4392560e31b815260206004820152600c60248201526b5f746172676574536861726560a01b6044820152606401610ee0565b612710611dfb83856159f8565b1115611e4a57604051630cb4392560e31b815260206004820181905260248201527f5f7374616b696e674d6f64756c65466565202b205f74726561737572794665656044820152606401610ee0565b6000611e558661380d565b805463ffffffff60c81b1916600160d81b61ffff8881169190910261ffff60c81b191691909117600160c81b868316021761ffff60b81b1916600160b81b918716919091021781556040805187815233602082015291925087917f065e5bd8e4145dd99cf69bad5871ad52d094aee07a67fcf2f418c89e49d5f20c910160405180910390a260408051858152602081018590523381830152905187917f303c8ac43d1b1f9b898ddd2915a294efa01e9b07c322d7deeb7db332b66f0410919081900360600190a2505050505050565b6000828152600080516020615ee183398151915260205260408120611f499083613c0e565b9392505050565b6000918252600080516020615f01833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600080611f948361380d565b90506000808260000160039054906101000a90046001600160a01b03166001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b158015611fe957600080fd5b505afa158015611ffd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612021919061595d565b5091509150612034836004015483613c1a565b61203e90826159e1565b95945050505050565b6000806000612054612853565b9450945050505061206e826001600160601b031682613c30565b9250505090565b60006120808261380d565b54600160e81b900460ff166002811115610d3c57610d3c615538565b7f706b9ed9846c161ad535be9b6345c3a7b2cb929e8d4a7254dee9ba6e6f8e5531546001600160a01b0316336001600160a01b0316146120ef57604051637e71782360e01b815260040160405180910390fd5b60006120f96116cd565b9050806121195760405163180a97cd60e21b815260040160405180910390fd5b60006121248561380d565b905060008154600160e81b900460ff16600281111561214557612145615538565b600281111561215657612156615538565b146121745760405163322e64fb60e11b815260040160405180910390fd5b60028101805467ffffffffffffffff1916426001600160401b0316179055436003820155604051348082529086907f9151b7f88aca05d432bb395647ef52b2ffc454e3c6afb69c95345af6b5a778c09060200160405180910390a26121e46a1a784379d99db42000000088615ae7565b811461220d5760405163023db95b60e21b81526004810182905260248101889052604401610ee0565b86156122fb5781546040516317dc836b60e31b8152600091829163010000009091046001600160a01b03169063bee41b5890612251908c908b908b90600401615b06565b600060405180830381600087803b15801561226b57600080fd5b505af115801561227f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122a79190810190615b83565b9150915060004790506122dd8a876040516020016122c791815260200190565b6040516020818303038152906040528585613c49565b47846122e982846159e1565b146122f6576122f6615bdc565b505050505b50505050505050565b61230c614d96565b600061231784612bce565b9050600081602001519050600080600080600080600080886001600160a01b031663b3076c3c8d6040518263ffffffff1660e01b815260040161235c91815260200190565b6101006040518083038186803b15801561237557600080fd5b505afa158015612389573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ad91906158f3565b97509750975097509750975097509750878b6000019015159081151581525050868b6020018181525050858b6040018181525050848b6060018181525050838b6080018181525050828b60a0018181525050818b60c0018181525050808b60e00181815250505050505050505050505092915050565b6000600080516020615f2183398151915261243e81336137a9565b8483146124685760405163098b37e560e31b81526004810186905260248101849052604401610ee0565b6000805b86811015612672576000888883818110612488576124886159cb565b905060200201359050600061249c8261380d565b6004810154909150808989868181106124b7576124b76159cb565b9050602002013510156124dd57604051632f789f4960e21b815260040160405180910390fd5b6000808360000160039054906101000a90046001600160a01b03166001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b15801561253057600080fd5b505afa158015612544573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612568919061595d565b5091509150808b8b88818110612580576125806159cb565b9050602002013511156125cb578a8a8781811061259f5761259f6159cb565b9050602002013581604051630b72c59d60e21b8152600401610ee0929190918252602082015260400190565b828b8b888181106125de576125de6159cb565b905060200201356125ef91906159e1565b6125f990886159f8565b96508282101561264157847fdd2523ca96a639ba7e17420698937f71eddd8af012ccb36ff5c8fe96141acae961262f84866159e1565b60405190815260200160405180910390a25b8a8a87818110612653576126536159cb565b905060200201358460040181905550856001019550505050505061246c565b509695505050505050565b7f779e5c23cb7a5bcb9bfe1e9a5165a00057f12bcdfd13e374540fdf1a1cd911376126a881336137a9565b8382146126d25760405163098b37e560e31b81526004810185905260248101839052604401610ee0565b60005b8481101561284b5760008484838181106126f1576126f16159cb565b905060200201351115612843576000612721878784818110612715576127156159cb565b9050602002013561380d565b54630100000090046001600160a01b0316905080638d7e401786868581811061274c5761274c6159cb565b905060200201356040518263ffffffff1660e01b815260040161277191815260200190565b600060405180830381600087803b15801561278b57600080fd5b505af192505050801561279c575060015b612841573d8080156127ca576040519150601f19603f3d011682016040523d82523d6000602084013e6127cf565b606091505b5080516127ef57604051638fd297d960e01b815260040160405180910390fd5b878784818110612801576128016159cb565b905060200201357ff74208fedac7280fd11f8de0be14e00423dc5076da8e8ec8ca90e09257fff1b3826040516128379190615bf2565b60405180910390a2505b505b6001016126d5565b505050505050565b6060806060600080600080612866613dc9565b80519193509150801580612878575082155b156128ba5750506040805160008082526020820181815282840182815260608401909452919850909650909450925068056bc75e2d631000009150612bc79050565b68056bc75e2d631000009350806001600160401b038111156128de576128de614ea7565b604051908082528060200260200182016040528015612907578160200160208202803683370190505b509650806001600160401b0381111561292257612922614ea7565b60405190808252806020026020018201604052801561294b578160200160208202803683370190505b509750806001600160401b0381111561296657612966614ea7565b60405190808252806020026020018201604052801561298f578160200160208202803683370190505b5095506000808060005b84811015612b945760008682815181106129b5576129b56159cb565b602002602001015160c001511115612b8c578581815181106129d9576129d96159cb565b60200260200101516020015162ffffff168b85815181106129fc576129fc6159cb565b6020026020010181815250508688878381518110612a1c57612a1c6159cb565b602002602001015160c00151612a329190615ae7565b612a3c91906159b7565b9250858181518110612a5057612a506159cb565b6020026020010151600001518c8581518110612a6e57612a6e6159cb565b60200260200101906001600160a01b031690816001600160a01b031681525050612710868281518110612aa357612aa36159cb565b60200260200101516040015161ffff1684612abe9190615ae7565b612ac891906159b7565b91506002868281518110612ade57612ade6159cb565b602002602001015160a001516002811115612afb57612afb615538565b14612b3457818a8581518110612b1357612b136159cb565b60200260200101906001600160601b031690816001600160601b0316815250505b81612710878381518110612b4a57612b4a6159cb565b60200260200101516060015161ffff1685612b659190615ae7565b612b6f91906159b7565b612b799190615c05565b612b83908a615c05565b98506001909301925b600101612999565b5086886001600160601b03161115612bae57612bae615bdc565b83831015612bc057828a52828b528289525b5050505050505b9091929394565b612bd6614cf4565b612bdf8261380d565b6040805161014081018252825462ffffff81168252630100000081046001600160a01b03166020830152600160b81b810461ffff90811693830193909352600160c81b810483166060830152600160d81b81049092166080820152600160e81b90910460ff1660a082015260018201805491929160c084019190612c6290615a97565b80601f0160208091040260200160405190810160405280929190818152602001828054612c8e90615a97565b8015612cdb5780601f10612cb057610100808354040283529160200191612cdb565b820191906000526020600020905b815481529060010190602001808311612cbe57829003601f168201915b505050918352505060028201546001600160401b031660208201526003820154604082015260049091015460609091015292915050565b60006060612d1f83613820565b509094909350915050565b600080516020615f21833981519152612d4381336137a9565b6000612d4e8761380d565b54630100000090046001600160a01b03169050612d6d86868686613e97565b604051634d8060a360e11b81526001600160a01b03821690639b00c14690612d9f908990899089908990600401615c27565b600060405180830381600087803b158015612db957600080fd5b505af11580156122f6573d6000803e3d6000fd5b6000818152600080516020615ee183398151915260205260408120610d3c90613f3d565b600080516020615f21833981519152612e0a81336137a9565b6000612e158761380d565b54630100000090046001600160a01b03169050612e3486868686613e97565b604051629b3d1960e81b81526001600160a01b03821690639b3d190090612d9f908990899089908990600401615c27565b600080516020615f41833981519152612e7e81336137a9565b6000612e898461380d565b9050826002811115612e9d57612e9d615538565b8154600160e81b900460ff166002811115612eba57612eba615538565b6002811115612ecb57612ecb615538565b1415612eea57604051635ca16fa760e11b815260040160405180910390fd5b612ef48184613b0f565b50505050565b612f038261110a565b612f0d81336137a9565b6111498383613a6d565b7f9a2f67efb89489040f2c48c3b2c38f719fba1276678d2ced3bd9049fb5edc6b2612f4281336137a9565b6000612f4d8361380d565b905060018154600160e81b900460ff166002811115612f6e57612f6e615538565b6002811115612f7f57612f7f615538565b14612f9d576040516316c1da1560e21b815260040160405180910390fd5b611149816000613b0f565b600080516020615f21833981519152612fc181336137a9565b6000612fcb61169e565b905060005b81811015611149576000612fe382613a9c565b905060008160000160039054906101000a90046001600160a01b031690506000816001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b15801561303c57600080fd5b505afa158015613050573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613074919061595d565b50509050826004015481141561316757816001600160a01b031663e864299e6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156130bf57600080fd5b505af19250505080156130d0575060015b613167573d8080156130fe576040519150601f19603f3d011682016040523d82523d6000602084013e613103565b606091505b50805161312357604051638fd297d960e01b815260040160405180910390fd5b835460405162ffffff909116907fe74bf895f0c3a2d6c74c40cbb362fdd9640035fc4226c72e3843809ad2a9d2b59061315d908490615bf2565b60405180910390a2505b836001019350505050612fd0565b600080516020615f4183398151915261318e81336137a9565b60006131998561380d565b5460405163a2e080f160e01b8152600481018690526024810185905263010000009091046001600160a01b03169150819063a2e080f190604401600060405180830381600087803b1580156131ed57600080fd5b505af1158015610fc1573d6000803e3d6000fd5b60006001611a8b565b7fe7c742a54cd11fc9749a47ab34bdcd7327820908e8d0d48b4a5c7f17b029409861323581336137a9565b61325e7fabeb05279af36da5d476d7f950157cd2ea98a4166fa68a6bc97ce3a22fbb93c0839055565b600061326861169e565b905060005b8181101561339357600061328082613a9c565b90508160010191508060000160039054906101000a90046001600160a01b03166001600160a01b03166390c09bdb6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156132da57600080fd5b505af19250505080156132eb575060015b61338d573d808015613319576040519150601f19603f3d011682016040523d82523d6000602084013e61331e565b606091505b50805161333e57604051638fd297d960e01b815260040160405180910390fd5b613349826001613b0f565b815460405162ffffff909116907f0d64b11929aa111ca874dd00b5b0cc2d82b741be924ec9e3691e67c71552f62390613383908490615bf2565b60405180910390a2505b5061326d565b50604080518481523360208201527f82e72df77173eab89b00556d791a407a78f4605c5c2f0694967c8c429dd43c7c91016118ab565b60008060008060006133d96136dc565b92506001600160601b031692506001600160601b031692506133fb8382613c30565b94506134078282613c30565b93505050509091565b6060600061341d84613787565b905082516001600160401b0381111561343857613438614ea7565b60405190808252806020026020018201604052801561347157816020015b61345e614ddd565b8152602001906001900390816134565790505b50915060005b835181101561359c57604051806060016040528085838151811061349d5761349d6159cb565b60200260200101518152602001836001600160a01b0316635e2fb9088785815181106134cb576134cb6159cb565b60200260200101516040518263ffffffff1660e01b81526004016134f191815260200190565b60206040518083038186803b15801561350957600080fd5b505afa15801561351d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135419190615c59565b1515815260200161356b8787858151811061355e5761355e6159cb565b6020026020010151612304565b815250838281518110613580576135806159cb565b60200260200101819052508061359590615acc565b9050613477565b505092915050565b606060006135b061169e565b9050806001600160401b038111156135ca576135ca614ea7565b6040519080825280602002602001820160405280156135f3578160200160208202803683370190505b50915060005b81811015611a825761360a81613a9c565b54835162ffffff90911690849083908110613627576136276159cb565b60209081029190910101526001016135f9565b6060600061364785613787565b604051634febc81b60e01b815260048101869052602481018590529091506000906001600160a01b03831690634febc81b9060440160006040518083038186803b15801561369457600080fd5b505afa1580156136a8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136d09190810190615c76565b90506111008682613410565b6000806000606060006136ed612853565b9650909450925060009150505b825181101561373a57828181518110613715576137156159cb565b6020026020010151866137289190615c05565b955061373381615acc565b90506136fa565b506137458582615cfb565b93505050909192565b9055565b60006001600160e01b03198216637965db0b60e01b1480610d3c57506301ffc9a760e01b6001600160e01b0319831614610d3c565b60006137928261380d565b54630100000090046001600160a01b031692915050565b6137b38282611f50565b611260576137cb816001600160a01b03166014613f47565b6137d6836020613f47565b6040516020016137e7929190615d23565b60408051601f198184030181529082905262461bcd60e51b8252610ee091600401615bf2565b6000610d3c61381b836139e5565b613a9c565b6000606080600061382f613dc9565b8051909350909150806001600160401b0381111561384f5761384f614ea7565b604051908082528060200260200182016040528015613878578160200160208202803683370190505b50935080156139dc5761388b86836159f8565b91506000816001600160401b038111156138a7576138a7614ea7565b6040519080825280602002602001820160405280156138d0578160200160208202803683370190505b5090506000805b838110156139cb578581815181106138f1576138f16159cb565b602002602001015160c0015187828151811061390f5761390f6159cb565b60200260200101818152505061271085878381518110613931576139316159cb565b60200260200101516080015161ffff1661394b9190615ae7565b61395591906159b7565b91506139a68287838151811061396d5761396d6159cb565b602002602001015160e0015188848151811061398b5761398b6159cb565b602002602001015160c001516139a191906159f8565b6140e2565b8382815181106139b8576139b86159cb565b60209081029190910101526001016138d7565b506139d786838a6140f1565b965050505b50509193909250565b60008181527f9b48f5b32acb95b982effe269feac267eead113c4b5af14ffeb9aadac18a6e9c6020819052604082205480613a3357604051636a0eb14160e11b815260040160405180910390fd5b6111de6001826159e1565b613a488282614136565b6000828152600080516020615ee18339815191526020526040902061114990826141ac565b613a7782826141c1565b6000828152600080516020615ee1833981519152602052604090206111499082614235565b60009081527f1d2f69fc9b5fe89d7414bf039e8d897c4c487c7603d80de6bcdd2868466f94766020526040902090565b7f9b48f5b32acb95b982effe269feac267eead113c4b5af14ffeb9aadac18a6e9c613af88260016159f8565b600093845260209190915260409092209190915550565b8154600090600160e81b900460ff166002811115613b2f57613b2f615538565b9050816002811115613b4357613b43615538565b816002811115613b5557613b55615538565b1461114957816002811115613b6c57613b6c615538565b835460ff91909116600160e81b0260ff60e81b1982168117855560405162ffffff9182169190921617907ffd6f15fb2b48a21a60fe3d44d3c3a0433ca01e121b5124a63ec45c30ad925a1790613bc59085903390615d92565b60405180910390a2505050565b613bda611d69565b15613bf85760405163184e52a160e21b815260040160405180910390fd5b613c018161424a565b50565b6112608282613a3e565b6000611f4983836142a9565b6000818311613c295781611f49565b5090919050565b600081613c3f61271085615ae7565b611f4991906159b7565b613c54846030615ae7565b825114613c8a578151613c68856030615ae7565b6040516346b38e7960e11b815260048101929092526024820152604401610ee0565b613c95846060615ae7565b815114613ccb578051613ca9856060615ae7565b604051633c11c1f760e21b815260048101929092526024820152604401610ee0565b6000613cd760306142d3565b90506000613ce560606142d3565b905060005b868110156122fb57613d0b8584613d02603085615ae7565b600060306142ec565b613d248483613d1b606085615ae7565b600060606142ec565b7f00000000000000000000000036936936936936936936936936936936936936936001600160a01b031663228951186a1a784379d99db420000000858986613d6d8c8a8a614373565b6040518663ffffffff1660e01b8152600401613d8c9493929190615db8565b6000604051808303818588803b158015613da557600080fd5b505af1158015613db9573d6000803e3d6000fd5b5050505050806001019050613cea565b600060606000613dd761169e565b9050806001600160401b03811115613df157613df1614ea7565b604051908082528060200260200182016040528015613e2a57816020015b613e17614dfc565b815260200190600190039081613e0f5790505b50915060005b81811015613e9157613e41816146ef565b838281518110613e5357613e536159cb565b6020026020010181905250828181518110613e7057613e706159cb565b602002602001015160c0015184613e8791906159f8565b9350600101613e30565b50509091565b613ea2600884615e03565b151580613eb85750613eb5601082615e03565b15155b15613ed9576040516363209a7d60e11b815260036004820152602401610ee0565b6000613ee66008856159b7565b905080613ef46010846159b7565b14613f15576040516363209a7d60e11b815260026004820152602401610ee0565b80613f36576040516363209a7d60e11b815260016004820152602401610ee0565b5050505050565b6000610d3c825490565b60606000613f56836002615ae7565b613f619060026159f8565b6001600160401b03811115613f7857613f78614ea7565b6040519080825280601f01601f191660200182016040528015613fa2576020820181803683370190505b509050600360fc1b81600081518110613fbd57613fbd6159cb565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110613fec57613fec6159cb565b60200101906001600160f81b031916908160001a9053506000614010846002615ae7565b61401b9060016159f8565b90505b6001811115614093576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061404f5761404f6159cb565b1a60f81b828281518110614065576140656159cb565b60200101906001600160f81b031916908160001a90535060049490941c9361408c81615e17565b905061401e565b508315611f495760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610ee0565b6000818310613c295781611f49565b6000805b8282101561412e57614111858561410c85876159e1565b61486e565b90508061411d5761412e565b61412781836159f8565b91506140f5565b509392505050565b6141408282611f50565b611260576000828152600080516020615f01833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6000611f49836001600160a01b038416614aac565b6141cb8282611f50565b15611260576000828152600080516020615f01833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611f49836001600160a01b038416614afb565b6142737f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6829055565b6040518181527ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9060200160405180910390a150565b60008260000182815481106142c0576142c06159cb565b9060005260206000200154905092915050565b60408051828152603f92810192909201601f1916905290565b84516142f882856159f8565b111580156143105750835161430d82846159f8565b11155b61435c5760405162461bcd60e51b815260206004820152601960248201527f42595445535f41525241595f4f55545f4f465f424f554e4453000000000000006044820152606401610ee0565b6020838601810190838601016122fb828285614bee565b60008061438060406142d3565b90506000614398614393604060606159e1565b6142d3565b90506143a9848360008060406142ec565b6143c28482604060006143bd8260606159e1565b6142ec565b6000600286600060801b6040516020016143dd929190615e2e565b60408051601f19818403018152908290526143f791615e66565b602060405180830381855afa158015614414573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061443791906158da565b905060006002808560405160200161444f9190615e66565b60408051601f198184030181529082905261446991615e66565b602060405180830381855afa158015614486573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906144a991906158da565b6040516002906144c0908790600090602001615e82565b60408051601f19818403018152908290526144da91615e66565b602060405180830381855afa1580156144f7573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061451a91906158da565b60408051602081019390935282015260600160408051601f198184030181529082905261454691615e66565b602060405180830381855afa158015614563573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061458691906158da565b9050600280838a60405160200161459e929190615ea4565b60408051601f19818403018152908290526145b891615e66565b602060405180830381855afa1580156145d5573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906145f891906158da565b6040805164d098d4af7160c81b60208201526000602882015290810184905260029060600160408051601f198184030181529082905261463791615e66565b602060405180830381855afa158015614654573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061467791906158da565b60408051602081019390935282015260600160408051601f19818403018152908290526146a391615e66565b602060405180830381855afa1580156146c0573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906146e391906158da565b98975050505050505050565b6146f7614dfc565b600061470283613a9c565b80546001600160a01b036301000000820416845262ffffff8116602085015261ffff600160b81b820481166040860152600160c81b820481166060860152600160d81b820416608085015290915060ff600160e81b90910416600281111561476c5761476c615538565b8260a00190600281111561478257614782615538565b9081600281111561479557614795615538565b81525050600080600084600001516001600160a01b0316639abddf096040518163ffffffff1660e01b815260040160606040518083038186803b1580156147db57600080fd5b505afa1580156147ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614813919061595d565b9194509250905060008560a00151600281111561483257614832615538565b1461483e576000614840565b805b60e08601526004840154614855908490613c1a565b61485f90836159e1565b60c08601525092949350505050565b825160009060001982846148885760009350505050611f49565b60005b875181101561495a578681815181106148a6576148a66159cb565b60200260200101518882815181106148c0576148c06159cb565b6020026020010151106148d25761494a565b8781815181106148e4576148e46159cb565b602002602001015183111561491b578093506001915087818151811061490c5761490c6159cb565b6020026020010151925061494a565b87818151811061492d5761492d6159cb565b602002602001015183141561494a576149476001836159f8565b91505b61495381615acc565b905061488b565b508061496c5760009350505050611f49565b60001960005b8851811015614a2b5787818151811061498d5761498d6159cb565b60200260200101518982815181106149a7576149a76159cb565b6020026020010151106149b957614a1b565b838982815181106149cc576149cc6159cb565b60200260200101511180156149f95750818982815181106149ef576149ef6159cb565b6020026020010151105b15614a1b57888181518110614a1057614a106159cb565b602002602001015191505b614a2481615acc565b9050614972565b50614a7560018311614a3d5786614a47565b614a478784614c39565b84614a6b848b8981518110614a5e57614a5e6159cb565b60200260200101516140e2565b6139a191906159e1565b945084888581518110614a8a57614a8a6159cb565b60200260200101818151614a9e91906159f8565b905250505050509392505050565b6000818152600183016020526040812054614af357508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610d3c565b506000610d3c565b60008181526001830160205260408120548015614be4576000614b1f6001836159e1565b8554909150600090614b33906001906159e1565b9050818114614b98576000866000018281548110614b5357614b536159cb565b9060005260206000200154905080876000018481548110614b7657614b766159cb565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614ba957614ba9615eca565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610d3c565b6000915050610d3c565b5b601f811115614c0f578251825260209283019290910190601f1901614bef565b80156111495782518251600019600160086020869003021b01908116901991909116178252505050565b60008215614c675781614c4d6001856159e1565b614c5791906159b7565b614c629060016159f8565b611f49565b50600092915050565b828054614c7c90615a97565b90600052602060002090601f016020900481019282614c9e5760008555614ce4565b82601f10614cb75782800160ff19823516178555614ce4565b82800160010185558215614ce4579182015b82811115614ce4578235825591602001919060010190614cc9565b50614cf0929150614e41565b5090565b604080516101408101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c082015260e08101829052610100810182905261012081019190915290565b60405180608001604052806000815260200160008152602001614d68614cf4565b8152602001614d9160405180606001604052806000815260200160008152602001600081525090565b905290565b604051806101000160405280600015158152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160608101825260008082526020820152908101614d91614d96565b604080516101008101825260008082526020820181905291810182905260608101829052608081018290529060a0820190815260200160008152602001600081525090565b5b80821115614cf05760008155600101614e42565b600060208284031215614e6857600080fd5b81356001600160e01b031981168114611f4957600080fd5b600060208284031215614e9257600080fd5b5035919050565b8015158114613c0157600080fd5b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b0381118282101715614edf57614edf614ea7565b60405290565b604051601f8201601f191681016001600160401b0381118282101715614f0d57614f0d614ea7565b604052919050565b600080600080848603610120811215614f2d57600080fd5b85359450602086013593506040860135614f4681614e99565b925060c0605f1982011215614f5a57600080fd5b50614f63614ebd565b606086013581526080860135602082015260a0860135604082015260c0860135606082015260e0860135608082015261010086013560a08201528091505092959194509250565b81518152602080830151908201526040808301519082015260608101610d3c565b60008060408385031215614fde57600080fd5b50508035926020909101359150565b80356001600160a01b038116811461500457600080fd5b919050565b6000806040838503121561501c57600080fd5b8235915061502c60208401614fed565b90509250929050565b8051151582526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301525050565b602080825282518282018190526000919060409081850190868401855b828110156150de57815180518552868101511515878601528501516150c986860182615035565b506101409390930192908501906001016150a2565b5091979650505050505050565b60008083601f8401126150fd57600080fd5b5081356001600160401b0381111561511457600080fd5b60208301915083602082850101111561512c57600080fd5b9250929050565b60008060008060008060a0878903121561514c57600080fd5b86356001600160401b0381111561516257600080fd5b61516e89828a016150eb565b9097509550615181905060208801614fed565b93506040870135925060608701359150608087013590509295509295509295565b60005b838110156151bd5781810151838201526020016151a5565b83811115612ef45750506000910152565b600081518084526151e68160208601602086016151a2565b601f01601f19169290920160200192915050565b805162ffffff1682526000610140602083015161522260208601826001600160a01b03169052565b506040830151615238604086018261ffff169052565b50606083015161524e606086018261ffff169052565b506080830151615264608086018261ffff169052565b5060a083015161527960a086018260ff169052565b5060c08301518160c0860152615291828601826151ce565b91505060e08301516152ae60e08601826001600160401b03169052565b5061010083810151908501526101209283015192909301919091525090565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561536457603f19898403018552815160c081518552888201518986015287820151818987015261532a828701826151fa565b60609384015180518886015260208101516080890152604081015160a08901529390925090505095880195935050908601906001016152f4565b509098975050505050505050565b60008060006060848603121561538757600080fd5b61539084614fed565b925061539e60208501614fed565b9150604084013590509250925092565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561540357603f198886030184526153f18583516151fa565b945092850192908501906001016153d5565b5092979650505050505050565b6000806000806080858703121561542657600080fd5b8435935060208501359250604085013561543f81614e99565b9396929550929360600135925050565b60006001600160401b0382111561546857615468614ea7565b5060051b60200190565b600082601f83011261548357600080fd5b813560206154986154938361544f565b614ee5565b82815260059290921b840181019181810190868411156154b757600080fd5b8286015b8481101561267257803583529183019183016154bb565b6000602082840312156154e457600080fd5b81356001600160401b038111156154fa57600080fd5b6111de84828501615472565b6000806000806080858703121561551c57600080fd5b5050823594602084013594506040840135936060013592509050565b634e487b7160e01b600052602160045260246000fd5b6003811061556c57634e487b7160e01b600052602160045260246000fd5b9052565b60208101610d3c828461554e565b6000806000806060858703121561559457600080fd5b843593506020850135925060408501356001600160401b038111156155b857600080fd5b6155c4878288016150eb565b95989497509550505050565b6101008101610d3c8284615035565b60008083601f8401126155f157600080fd5b5081356001600160401b0381111561560857600080fd5b6020830191508360208260051b850101111561512c57600080fd5b6000806000806040858703121561563957600080fd5b84356001600160401b038082111561565057600080fd5b61565c888389016155df565b9096509450602087013591508082111561567557600080fd5b506155c4878288016155df565b600081518084526020808501945080840160005b838110156156b257815187529582019590820190600101615696565b509495945050505050565b60a0808252865190820181905260009060209060c0840190828a01845b828110156156ff5781516001600160a01b0316845292840192908401906001016156da565b505050838103828501526157138189615682565b8481036040860152875180825283890192509083019060005b818110156157515783516001600160601b03168352928401929184019160010161572c565b50506001600160601b0387166060860152925061576c915050565b8260808301529695505050505050565b602081526000611f4960208301846151fa565b8281526040602082015260006111de6040830184615682565b6000806000806000606086880312156157c057600080fd5b8535945060208601356001600160401b03808211156157de57600080fd5b6157ea89838a016150eb565b9096509450604088013591508082111561580357600080fd5b50615810888289016150eb565b969995985093965092949392505050565b6000806040838503121561583457600080fd5b8235915060208301356003811061584a57600080fd5b809150509250929050565b60008060006060848603121561586a57600080fd5b505081359360208301359350604090920135919050565b6000806040838503121561589457600080fd5b8235915060208301356001600160401b038111156158b157600080fd5b6158bd85828601615472565b9150509250929050565b602081526000611f496020830184615682565b6000602082840312156158ec57600080fd5b5051919050565b600080600080600080600080610100898b03121561591057600080fd5b885161591b81614e99565b809850506020890151965060408901519550606089015194506080890151935060a0890151925060c0890151915060e089015190509295985092959890939650565b60008060006060848603121561597257600080fd5b8351925060208401519150604084015190509250925092565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000826159c6576159c661598b565b500490565b634e487b7160e01b600052603260045260246000fd5b6000828210156159f3576159f36159a1565b500390565b60008219821115615a0b57615a0b6159a1565b500190565b600062ffffff808316818516808303821115615a2e57615a2e6159a1565b01949350505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600060018060a01b03808716835260606020840152615a83606084018688615a37565b915080841660408401525095945050505050565b600181811c90821680615aab57607f821691505b60208210811415611d6357634e487b7160e01b600052602260045260246000fd5b6000600019821415615ae057615ae06159a1565b5060010190565b6000816000190483118215151615615b0157615b016159a1565b500290565b83815260406020820152600061203e604083018486615a37565b600082601f830112615b3157600080fd5b81516001600160401b03811115615b4a57615b4a614ea7565b615b5d601f8201601f1916602001614ee5565b818152846020838601011115615b7257600080fd5b6111de8260208301602087016151a2565b60008060408385031215615b9657600080fd5b82516001600160401b0380821115615bad57600080fd5b615bb986838701615b20565b93506020850151915080821115615bcf57600080fd5b506158bd85828601615b20565b634e487b7160e01b600052600160045260246000fd5b602081526000611f4960208301846151ce565b60006001600160601b03808316818516808303821115615a2e57615a2e6159a1565b604081526000615c3b604083018688615a37565b8281036020840152615c4e818587615a37565b979650505050505050565b600060208284031215615c6b57600080fd5b8151611f4981614e99565b60006020808385031215615c8957600080fd5b82516001600160401b03811115615c9f57600080fd5b8301601f81018513615cb057600080fd5b8051615cbe6154938261544f565b81815260059190911b82018301908381019087831115615cdd57600080fd5b928401925b82841015615c4e57835182529284019290840190615ce2565b60006001600160601b0383811690831681811015615d1b57615d1b6159a1565b039392505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351615d558160178501602088016151a2565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615d868160288401602088016151a2565b01602801949350505050565b60408101615da0828561554e565b6001600160a01b039290921660209190910152919050565b608081526000615dcb60808301876151ce565b8281036020840152615ddd81876151ce565b90508281036040840152615df181866151ce565b91505082606083015295945050505050565b600082615e1257615e1261598b565b500690565b600081615e2657615e266159a1565b506000190190565b60008351615e408184602088016151a2565b6fffffffffffffffffffffffffffffffff19939093169190920190815260100192915050565b60008251615e788184602087016151a2565b9190910192915050565b60008351615e948184602088016151a2565b9190910191825250602001919050565b82815260008251615ebc8160208501602087016151a2565b919091016020019392505050565b634e487b7160e01b600052603160045260246000fdfe8f8c450dae5029cd48cd91dd9db65da48fb742893edfc7941250f6721d93cbbe9a627a5d4aa7c17f87ff26e3fe9a42c2b6c559e8b41a42282d0ecebb17c0e4d3c23292b191d95d2a7dd94fc6436eb44338fda9e1307d9394fd27c28157c1b33c3105bcbf19d4417b73ae0e58d508a65ecf75665e46c2622d8521732de6080c48a2646970667358221220f4c91cf037a6c582ea19427dce3b228670f62711b8daea8e96ce318f5d88724c64736f6c63430008090033