Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
Amount
|
||
|---|---|---|---|---|---|---|---|
| Bulk Register Va... | 1809534 | 17 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1809415 | 18 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1809343 | 18 hrs ago | 0 ETH | ||||
| Withdraw | 1809291 | 18 hrs ago | 0 ETH | ||||
| Bulk Remove Vali... | 1809200 | 18 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1808888 | 20 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1808867 | 20 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1808844 | 20 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1808822 | 20 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1808803 | 20 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1808476 | 21 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1803589 | 38 hrs ago | 0 ETH | ||||
| Deposit | 1802594 | 42 hrs ago | 0 ETH | ||||
| Deposit | 1802593 | 42 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1801763 | 45 hrs ago | 0 ETH | ||||
| Bulk Remove Vali... | 1801187 | 47 hrs ago | 0 ETH | ||||
| Bulk Register Va... | 1800912 | 2 days ago | 0 ETH | ||||
| Bulk Remove Vali... | 1800536 | 2 days ago | 0 ETH | ||||
| Bulk Register Va... | 1800396 | 2 days ago | 0 ETH | ||||
| Bulk Register Va... | 1800383 | 2 days ago | 0 ETH | ||||
| Bulk Register Va... | 1800372 | 2 days ago | 0 ETH | ||||
| Bulk Remove Vali... | 1800278 | 2 days ago | 0 ETH | ||||
| Bulk Register Va... | 1799333 | 2 days ago | 0 ETH | ||||
| Bulk Register Va... | 1799322 | 2 days ago | 0 ETH | ||||
| Bulk Register Va... | 1799317 | 2 days ago | 0 ETH |
Loading...
Loading
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
SSVClusters
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 10000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
import {ISSVClusters} from "../interfaces/ISSVClusters.sol";
import "../libraries/ClusterLib.sol";
import "../libraries/OperatorLib.sol";
import "../libraries/ProtocolLib.sol";
import "../libraries/CoreLib.sol";
import "../libraries/ValidatorLib.sol";
import {SSVStorage, StorageData} from "../libraries/SSVStorage.sol";
import {SSVStorageProtocol, StorageProtocol} from "../libraries/SSVStorageProtocol.sol";
contract SSVClusters is ISSVClusters {
using ClusterLib for Cluster;
using OperatorLib for Operator;
using ProtocolLib for StorageProtocol;
function registerValidator(
bytes calldata publicKey,
uint64[] memory operatorIds,
bytes calldata sharesData,
uint256 amount,
Cluster memory cluster
) external override {
StorageData storage s = SSVStorage.load();
StorageProtocol storage sp = SSVStorageProtocol.load();
ValidatorLib.validateOperatorsLength(operatorIds);
ValidatorLib.registerPublicKey(publicKey, operatorIds, s);
bytes32 hashedCluster = cluster.validateClusterOnRegistration(operatorIds, s);
cluster.balance += amount;
cluster.updateClusterOnRegistration(operatorIds, hashedCluster, 1, s, sp);
if (amount != 0) {
CoreLib.deposit(amount);
}
emit ValidatorAdded(msg.sender, operatorIds, publicKey, sharesData, cluster);
}
function bulkRegisterValidator(
bytes[] memory publicKeys,
uint64[] memory operatorIds,
bytes[] calldata sharesData,
uint256 amount,
Cluster memory cluster
) external override {
uint256 validatorsLength = publicKeys.length;
if (validatorsLength == 0) revert EmptyPublicKeysList();
if (validatorsLength != sharesData.length) revert PublicKeysSharesLengthMismatch();
StorageData storage s = SSVStorage.load();
StorageProtocol storage sp = SSVStorageProtocol.load();
ValidatorLib.validateOperatorsLength(operatorIds);
for (uint i; i < validatorsLength; ++i) {
ValidatorLib.registerPublicKey(publicKeys[i], operatorIds, s);
}
bytes32 hashedCluster = cluster.validateClusterOnRegistration(operatorIds, s);
cluster.balance += amount;
cluster.updateClusterOnRegistration(operatorIds, hashedCluster, uint32(validatorsLength), s, sp);
if (amount != 0) {
CoreLib.deposit(amount);
}
for (uint i; i < validatorsLength; ++i) {
bytes memory pk = publicKeys[i];
bytes memory sh = sharesData[i];
emit ValidatorAdded(msg.sender, operatorIds, pk, sh, cluster);
}
}
function removeValidator(
bytes calldata publicKey,
uint64[] memory operatorIds,
Cluster memory cluster
) external override {
StorageData storage s = SSVStorage.load();
bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s);
bytes32 hashedOperatorIds = ValidatorLib.hashOperatorIds(operatorIds);
bytes32 hashedValidator = keccak256(abi.encodePacked(publicKey, msg.sender));
bytes32 validatorData = s.validatorPKs[hashedValidator];
if (validatorData == bytes32(0)) {
revert ISSVNetworkCore.ValidatorDoesNotExist();
}
if (!ValidatorLib.validateCorrectState(validatorData, hashedOperatorIds))
revert ISSVNetworkCore.IncorrectValidatorStateWithData(publicKey);
delete s.validatorPKs[hashedValidator];
if (cluster.active) {
StorageProtocol storage sp = SSVStorageProtocol.load();
(uint64 clusterIndex, ) = OperatorLib.updateClusterOperators(operatorIds, false, 1, s, sp);
cluster.updateClusterData(clusterIndex, sp.currentNetworkFeeIndex());
sp.updateDAO(false, 1);
}
--cluster.validatorCount;
s.clusters[hashedCluster] = cluster.hashClusterData();
emit ValidatorRemoved(msg.sender, operatorIds, publicKey, cluster);
}
function bulkRemoveValidator(
bytes[] calldata publicKeys,
uint64[] memory operatorIds,
Cluster memory cluster
) external override {
uint256 validatorsLength = publicKeys.length;
if (validatorsLength == 0) {
revert ISSVNetworkCore.ValidatorDoesNotExist();
}
StorageData storage s = SSVStorage.load();
bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s);
bytes32 hashedOperatorIds = ValidatorLib.hashOperatorIds(operatorIds);
bytes32 hashedValidator;
bytes32 validatorData;
uint32 validatorsRemoved;
for (uint i; i < validatorsLength; ++i) {
hashedValidator = keccak256(abi.encodePacked(publicKeys[i], msg.sender));
validatorData = s.validatorPKs[hashedValidator];
if (!ValidatorLib.validateCorrectState(validatorData, hashedOperatorIds))
revert ISSVNetworkCore.IncorrectValidatorStateWithData(publicKeys[i]);
delete s.validatorPKs[hashedValidator];
validatorsRemoved++;
}
if (cluster.active) {
StorageProtocol storage sp = SSVStorageProtocol.load();
(uint64 clusterIndex, ) = OperatorLib.updateClusterOperators(operatorIds, false, validatorsRemoved, s, sp);
cluster.updateClusterData(clusterIndex, sp.currentNetworkFeeIndex());
sp.updateDAO(false, validatorsRemoved);
}
cluster.validatorCount -= validatorsRemoved;
s.clusters[hashedCluster] = cluster.hashClusterData();
for (uint i; i < validatorsLength; ++i) {
emit ValidatorRemoved(msg.sender, operatorIds, publicKeys[i], cluster);
}
}
function liquidate(address clusterOwner, uint64[] calldata operatorIds, Cluster memory cluster) external override {
StorageData storage s = SSVStorage.load();
bytes32 hashedCluster = cluster.validateHashedCluster(clusterOwner, operatorIds, s);
cluster.validateClusterIsNotLiquidated();
StorageProtocol storage sp = SSVStorageProtocol.load();
(uint64 clusterIndex, uint64 burnRate) = OperatorLib.updateClusterOperators(
operatorIds,
false,
cluster.validatorCount,
s,
sp
);
cluster.updateBalance(clusterIndex, sp.currentNetworkFeeIndex());
uint256 balanceLiquidatable;
if (
clusterOwner != msg.sender &&
!cluster.isLiquidatable(
burnRate,
sp.networkFee,
sp.minimumBlocksBeforeLiquidation,
sp.minimumLiquidationCollateral
)
) {
revert ClusterNotLiquidatable();
}
sp.updateDAO(false, cluster.validatorCount);
if (cluster.balance != 0) {
balanceLiquidatable = cluster.balance;
cluster.balance = 0;
}
cluster.index = 0;
cluster.networkFeeIndex = 0;
cluster.active = false;
s.clusters[hashedCluster] = cluster.hashClusterData();
if (balanceLiquidatable != 0) {
CoreLib.transferBalance(msg.sender, balanceLiquidatable);
}
emit ClusterLiquidated(clusterOwner, operatorIds, cluster);
}
function reactivate(uint64[] calldata operatorIds, uint256 amount, Cluster memory cluster) external override {
StorageData storage s = SSVStorage.load();
bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s);
if (cluster.active) revert ClusterAlreadyEnabled();
StorageProtocol storage sp = SSVStorageProtocol.load();
(uint64 clusterIndex, uint64 burnRate) = OperatorLib.updateClusterOperators(
operatorIds,
true,
cluster.validatorCount,
s,
sp
);
cluster.balance += amount;
cluster.active = true;
cluster.index = clusterIndex;
cluster.networkFeeIndex = sp.currentNetworkFeeIndex();
sp.updateDAO(true, cluster.validatorCount);
if (
cluster.isLiquidatable(
burnRate,
sp.networkFee,
sp.minimumBlocksBeforeLiquidation,
sp.minimumLiquidationCollateral
)
) {
revert InsufficientBalance();
}
s.clusters[hashedCluster] = cluster.hashClusterData();
if (amount > 0) {
CoreLib.deposit(amount);
}
emit ClusterReactivated(msg.sender, operatorIds, cluster);
}
function deposit(
address clusterOwner,
uint64[] calldata operatorIds,
uint256 amount,
Cluster memory cluster
) external override {
StorageData storage s = SSVStorage.load();
bytes32 hashedCluster = cluster.validateHashedCluster(clusterOwner, operatorIds, s);
cluster.balance += amount;
s.clusters[hashedCluster] = cluster.hashClusterData();
CoreLib.deposit(amount);
emit ClusterDeposited(clusterOwner, operatorIds, amount, cluster);
}
function withdraw(uint64[] calldata operatorIds, uint256 amount, Cluster memory cluster) external override {
StorageData storage s = SSVStorage.load();
bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s);
cluster.validateClusterIsNotLiquidated();
StorageProtocol storage sp = SSVStorageProtocol.load();
uint64 burnRate;
if (cluster.active) {
uint64 clusterIndex;
{
uint256 operatorsLength = operatorIds.length;
for (uint256 i; i < operatorsLength; ++i) {
Operator storage operator = SSVStorage.load().operators[operatorIds[i]];
clusterIndex +=
operator.snapshot.index +
(uint64(block.number) - operator.snapshot.block) *
operator.fee;
burnRate += operator.fee;
}
}
cluster.updateClusterData(clusterIndex, sp.currentNetworkFeeIndex());
}
if (cluster.balance < amount) revert InsufficientBalance();
cluster.balance -= amount;
if (
cluster.active &&
cluster.validatorCount != 0 &&
cluster.isLiquidatable(
burnRate,
sp.networkFee,
sp.minimumBlocksBeforeLiquidation,
sp.minimumLiquidationCollateral
)
) {
revert InsufficientBalance();
}
s.clusters[hashedCluster] = cluster.hashClusterData();
CoreLib.transferBalance(msg.sender, amount);
emit ClusterWithdrawn(msg.sender, operatorIds, amount, cluster);
}
function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external override {
if (
!ValidatorLib.validateCorrectState(
SSVStorage.load().validatorPKs[keccak256(abi.encodePacked(publicKey, msg.sender))],
ValidatorLib.hashOperatorIds(operatorIds)
)
) revert ISSVNetworkCore.IncorrectValidatorStateWithData(publicKey);
emit ValidatorExited(msg.sender, operatorIds, publicKey);
}
function bulkExitValidator(bytes[] calldata publicKeys, uint64[] calldata operatorIds) external override {
if (publicKeys.length == 0) {
revert ISSVNetworkCore.ValidatorDoesNotExist();
}
bytes32 hashedOperatorIds = ValidatorLib.hashOperatorIds(operatorIds);
for (uint i; i < publicKeys.length; ++i) {
if (
!ValidatorLib.validateCorrectState(
SSVStorage.load().validatorPKs[keccak256(abi.encodePacked(publicKeys[i], msg.sender))],
hashedOperatorIds
)
) revert ISSVNetworkCore.IncorrectValidatorStateWithData(publicKeys[i]);
emit ValidatorExited(msg.sender, operatorIds, publicKeys[i]);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
pragma solidity ^0.8.0;
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
*/
library Counters {
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/ERC165Checker.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
/**
* @dev Returns true if `account` supports the {IERC165} interface.
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
!supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
}
/**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*
* _Available since v3.4._
*/
function getSupportedInterfaces(
address account,
bytes4[] memory interfaceIds
) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
// prepare call
bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);
// perform static call
bool success;
uint256 returnSize;
uint256 returnValue;
assembly {
success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
returnSize := returndatasize()
returnValue := mload(0x00)
}
return success && returnSize >= 0x20 && returnValue > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;
interface ISSVWhitelistingContract {
/// @notice Checks if the caller is whitelisted
/// @param account The account that is being checked for whitelisting
/// @param operatorId The SSV Operator Id which is being checked
function isWhitelisted(address account, uint256 operatorId) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;
import {ISSVNetworkCore} from "./ISSVNetworkCore.sol";
interface ISSVClusters is ISSVNetworkCore {
/// @notice Registers a new validator on the SSV Network
/// @param publicKey The public key of the new validator
/// @param operatorIds Array of IDs of operators managing this validator
/// @param sharesData Encrypted shares related to the new validator
/// @param amount Amount of SSV tokens to be deposited
/// @param cluster Cluster to be used with the new validator
function registerValidator(
bytes calldata publicKey,
uint64[] memory operatorIds,
bytes calldata sharesData,
uint256 amount,
Cluster memory cluster
) external;
/// @notice Registers new validators on the SSV Network
/// @param publicKeys The public keys of the new validators
/// @param operatorIds Array of IDs of operators managing this validator
/// @param sharesData Encrypted shares related to the new validators
/// @param amount Amount of SSV tokens to be deposited
/// @param cluster Cluster to be used with the new validator
function bulkRegisterValidator(
bytes[] calldata publicKeys,
uint64[] memory operatorIds,
bytes[] calldata sharesData,
uint256 amount,
Cluster memory cluster
) external;
/// @notice Removes an existing validator from the SSV Network
/// @param publicKey The public key of the validator to be removed
/// @param operatorIds Array of IDs of operators managing the validator
/// @param cluster Cluster associated with the validator
function removeValidator(bytes calldata publicKey, uint64[] memory operatorIds, Cluster memory cluster) external;
/// @notice Bulk removes a set of existing validators in the same cluster from the SSV Network
/// @notice Reverts if publicKeys contains duplicates or non-existent validators
/// @param publicKeys The public keys of the validators to be removed
/// @param operatorIds Array of IDs of operators managing the validator
/// @param cluster Cluster associated with the validator
function bulkRemoveValidator(
bytes[] calldata publicKeys,
uint64[] memory operatorIds,
Cluster memory cluster
) external;
/**************************/
/* Cluster External Functions */
/**************************/
/// @notice Liquidates a cluster
/// @param owner The owner of the cluster
/// @param operatorIds Array of IDs of operators managing the cluster
/// @param cluster Cluster to be liquidated
function liquidate(address owner, uint64[] memory operatorIds, Cluster memory cluster) external;
/// @notice Reactivates a cluster
/// @param operatorIds Array of IDs of operators managing the cluster
/// @param amount Amount of SSV tokens to be deposited for reactivation
/// @param cluster Cluster to be reactivated
function reactivate(uint64[] memory operatorIds, uint256 amount, Cluster memory cluster) external;
/******************************/
/* Balance External Functions */
/******************************/
/// @notice Deposits tokens into a cluster
/// @param owner The owner of the cluster
/// @param operatorIds Array of IDs of operators managing the cluster
/// @param amount Amount of SSV tokens to be deposited
/// @param cluster Cluster where the deposit will be made
function deposit(address owner, uint64[] memory operatorIds, uint256 amount, Cluster memory cluster) external;
/// @notice Withdraws tokens from a cluster
/// @param operatorIds Array of IDs of operators managing the cluster
/// @param tokenAmount Amount of SSV tokens to be withdrawn
/// @param cluster Cluster where the withdrawal will be made
function withdraw(uint64[] memory operatorIds, uint256 tokenAmount, Cluster memory cluster) external;
/// @notice Fires the exit event for a validator
/// @param publicKey The public key of the validator to be exited
/// @param operatorIds Array of IDs of operators managing the validator
function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external;
/// @notice Fires the exit event for a set of validators
/// @param publicKeys The public keys of the validators to be exited
/// @param operatorIds Array of IDs of operators managing the validators
function bulkExitValidator(bytes[] calldata publicKeys, uint64[] calldata operatorIds) external;
/**
* @dev Emitted when the validator has been added.
* @param publicKey The public key of a validator.
* @param operatorIds The operator ids list.
* @param shares snappy compressed shares(a set of encrypted and public shares).
* @param cluster All the cluster data.
*/
event ValidatorAdded(address indexed owner, uint64[] operatorIds, bytes publicKey, bytes shares, Cluster cluster);
/**
* @dev Emitted when the validator is removed.
* @param publicKey The public key of a validator.
* @param operatorIds The operator ids list.
* @param cluster All the cluster data.
*/
event ValidatorRemoved(address indexed owner, uint64[] operatorIds, bytes publicKey, Cluster cluster);
/**
* @dev Emitted when a cluster is liquidated.
* @param owner The owner of the liquidated cluster.
* @param operatorIds The operator IDs managing the cluster.
* @param cluster The liquidated cluster data.
*/
event ClusterLiquidated(address indexed owner, uint64[] operatorIds, Cluster cluster);
/**
* @dev Emitted when a cluster is reactivated.
* @param owner The owner of the reactivated cluster.
* @param operatorIds The operator IDs managing the cluster.
* @param cluster The reactivated cluster data.
*/
event ClusterReactivated(address indexed owner, uint64[] operatorIds, Cluster cluster);
/**
* @dev Emitted when tokens are withdrawn from a cluster.
* @param owner The owner of the cluster.
* @param operatorIds The operator IDs managing the cluster.
* @param value The amount of tokens withdrawn.
* @param cluster The cluster from which tokens were withdrawn.
*/
event ClusterWithdrawn(address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster);
/**
* @dev Emitted when tokens are deposited into a cluster.
* @param owner The owner of the cluster.
* @param operatorIds The operator IDs managing the cluster.
* @param value The amount of SSV tokens deposited.
* @param cluster The cluster into which SSV tokens were deposited.
*/
event ClusterDeposited(address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster);
/**
* @dev Emitted when a validator begins the exit process.
* @param owner The owner of the exiting validator.
* @param operatorIds The operator IDs managing the validator.
* @param publicKey The public key of the exiting validator.
*/
event ValidatorExited(address indexed owner, uint64[] operatorIds, bytes publicKey);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;
interface ISSVNetworkCore {
/***********/
/* Structs */
/***********/
/// @notice Represents a snapshot of an operator's or a DAO's state at a certain block
struct Snapshot {
/// @dev The block number when the snapshot was taken
uint32 block;
/// @dev The last index calculated by the formula index += (currentBlock - block) * fee
uint64 index;
/// @dev Total accumulated earnings calculated by the formula accumulated + lastIndex * validatorCount
uint64 balance;
}
/// @notice Represents an SSV operator
struct Operator {
/// @dev The number of validators associated with this operator
uint32 validatorCount;
/// @dev The fee charged by the operator, set to zero for private operators and cannot be increased once set
uint64 fee;
/// @dev The address of the operator's owner
address owner;
/// @dev private flag for this operator
bool whitelisted;
/// @dev The state snapshot of the operator
Snapshot snapshot;
}
/// @notice Represents a request to change an operator's fee
struct OperatorFeeChangeRequest {
/// @dev The new fee proposed by the operator
uint64 fee;
/// @dev The time when the approval period for the fee change begins
uint64 approvalBeginTime;
/// @dev The time when the approval period for the fee change ends
uint64 approvalEndTime;
}
/// @notice Represents a cluster of validators
struct Cluster {
/// @dev The number of validators in the cluster
uint32 validatorCount;
/// @dev The index of network fees related to this cluster
uint64 networkFeeIndex;
/// @dev The last index calculated for the cluster
uint64 index;
/// @dev Flag indicating whether the cluster is active
bool active;
/// @dev The balance of the cluster
uint256 balance;
}
/**********/
/* Errors */
/**********/
error CallerNotOwnerWithData(address caller, address owner); // 0x163678e9
error CallerNotWhitelistedWithData(uint64 operatorId); // 0xb7f529fe
error FeeTooLow(); // 0x732f9413
error FeeExceedsIncreaseLimit(); // 0x958065d9
error NoFeeDeclared(); // 0x1d226c30
error ApprovalNotWithinTimeframe(); // 0x97e4b518
error OperatorDoesNotExist(); // 0x961e3e8c
error InsufficientBalance(); // 0xf4d678b8
error ValidatorDoesNotExist(); // 0xe51315d2
error ClusterNotLiquidatable(); // 0x60300a8d
error InvalidPublicKeyLength(); // 0x637297a4
error InvalidOperatorIdsLength(); // 0x38186224
error ClusterAlreadyEnabled(); // 0x3babafd2
error ClusterIsLiquidated(); // 0x95a0cf33
error ClusterDoesNotExists(); // 0x185e2b16
error IncorrectClusterState(); // 0x12e04c87
error UnsortedOperatorsList(); // 0xdd020e25
error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac
error ExceedValidatorLimitWithData(uint64 operatorId); // 0x8ddf7de4
error TokenTransferFailed(); // 0x045c4b02
error SameFeeChangeNotAllowed(); // 0xc81272f8
error FeeIncreaseNotAllowed(); // 0x410a2b6c
error NotAuthorized(); // 0xea8e4eb5
error OperatorsListNotUnique(); // 0xa5a1ff5d
error OperatorAlreadyExists(); // 0x289c9494
error TargetModuleDoesNotExistWithData(uint8 moduleId); // 0x208bb85d
error MaxValueExceeded(); // 0x91aa3017
error FeeTooHigh(); // 0xcd4e6167
error PublicKeysSharesLengthMismatch(); // 0x9ad467b8
error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938
error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999
error EmptyPublicKeysList(); // df83e679
error InvalidContractAddress(); // 0xa710429d
error AddressIsWhitelistingContract(address contractAddress); // 0x71cadba7
error InvalidWhitelistingContract(address contractAddress); // 0x886e6a03
error InvalidWhitelistAddressesLength(); // 0xcbb362dc
error ZeroAddressNotAllowed(); // 0x8579befe
// legacy errors
error ValidatorAlreadyExists(); // 0x8d09a73e
error IncorrectValidatorState(); // 0x2feda3c1
error ExceedValidatorLimit(uint64 operatorId); // 0x6df5ab76
error CallerNotOwner(); // 0x5cd83192
error TargetModuleDoesNotExist(); // 0x8f9195fb
error CallerNotWhitelisted(); // 0x8c6e5d71
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
import "../interfaces/ISSVNetworkCore.sol";
import {StorageData} from "./SSVStorage.sol";
import {StorageProtocol} from "./SSVStorageProtocol.sol";
import "./OperatorLib.sol";
import "./ProtocolLib.sol";
import {Types64} from "./Types.sol";
library ClusterLib {
using Types64 for uint64;
using ProtocolLib for StorageProtocol;
function updateBalance(
ISSVNetworkCore.Cluster memory cluster,
uint64 newIndex,
uint64 currentNetworkFeeIndex
) internal pure {
uint64 networkFee = uint64(currentNetworkFeeIndex - cluster.networkFeeIndex) * cluster.validatorCount;
uint64 usage = (newIndex - cluster.index) * cluster.validatorCount + networkFee;
cluster.balance = usage.expand() > cluster.balance ? 0 : cluster.balance - usage.expand();
}
function isLiquidatable(
ISSVNetworkCore.Cluster memory cluster,
uint64 burnRate,
uint64 networkFee,
uint64 minimumBlocksBeforeLiquidation,
uint64 minimumLiquidationCollateral
) internal pure returns (bool liquidatable) {
if (cluster.validatorCount != 0) {
if (cluster.balance < minimumLiquidationCollateral.expand()) return true;
uint64 liquidationThreshold = minimumBlocksBeforeLiquidation *
(burnRate + networkFee) *
cluster.validatorCount;
return cluster.balance < liquidationThreshold.expand();
}
}
function validateClusterIsNotLiquidated(ISSVNetworkCore.Cluster memory cluster) internal pure {
if (!cluster.active) revert ISSVNetworkCore.ClusterIsLiquidated();
}
function validateHashedCluster(
ISSVNetworkCore.Cluster memory cluster,
address owner,
uint64[] memory operatorIds,
StorageData storage s
) internal view returns (bytes32 hashedCluster) {
hashedCluster = keccak256(abi.encodePacked(owner, operatorIds));
bytes32 hashedClusterData = hashClusterData(cluster);
bytes32 clusterData = s.clusters[hashedCluster];
if (clusterData == bytes32(0)) {
revert ISSVNetworkCore.ClusterDoesNotExists();
} else if (clusterData != hashedClusterData) {
revert ISSVNetworkCore.IncorrectClusterState();
}
}
function updateClusterData(
ISSVNetworkCore.Cluster memory cluster,
uint64 clusterIndex,
uint64 currentNetworkFeeIndex
) internal pure {
updateBalance(cluster, clusterIndex, currentNetworkFeeIndex);
cluster.index = clusterIndex;
cluster.networkFeeIndex = currentNetworkFeeIndex;
}
function hashClusterData(ISSVNetworkCore.Cluster memory cluster) internal pure returns (bytes32) {
return
keccak256(
abi.encodePacked(
cluster.validatorCount,
cluster.networkFeeIndex,
cluster.index,
cluster.balance,
cluster.active
)
);
}
function validateClusterOnRegistration(
ISSVNetworkCore.Cluster memory cluster,
uint64[] memory operatorIds,
StorageData storage s
) internal view returns (bytes32 hashedCluster) {
hashedCluster = keccak256(abi.encodePacked(msg.sender, operatorIds));
bytes32 clusterData = s.clusters[hashedCluster];
if (clusterData == bytes32(0)) {
if (
cluster.validatorCount != 0 ||
cluster.networkFeeIndex != 0 ||
cluster.index != 0 ||
cluster.balance != 0 ||
!cluster.active
) {
revert ISSVNetworkCore.IncorrectClusterState();
}
} else if (clusterData != hashClusterData(cluster)) {
revert ISSVNetworkCore.IncorrectClusterState();
} else {
validateClusterIsNotLiquidated(cluster);
}
}
function updateClusterOnRegistration(
ISSVNetworkCore.Cluster memory cluster,
uint64[] memory operatorIds,
bytes32 hashedCluster,
uint32 validatorCountDelta,
StorageData storage s,
StorageProtocol storage sp
) internal {
(uint64 clusterIndex, uint64 burnRate) = OperatorLib.updateClusterOperatorsOnRegistration(
operatorIds,
validatorCountDelta,
s,
sp
);
updateClusterData(cluster, clusterIndex, sp.currentNetworkFeeIndex());
sp.updateDAO(true, validatorCountDelta);
cluster.validatorCount += validatorCountDelta;
if (
isLiquidatable(
cluster,
burnRate,
sp.networkFee,
sp.minimumBlocksBeforeLiquidation,
sp.minimumLiquidationCollateral
)
) {
revert ISSVNetworkCore.InsufficientBalance();
}
s.clusters[hashedCluster] = hashClusterData(cluster);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
import "./SSVStorage.sol";
library CoreLib {
event ModuleUpgraded(SSVModules indexed moduleId, address moduleAddress);
function getVersion() internal pure returns (string memory) {
return "v1.2.0";
}
function transferBalance(address to, uint256 amount) internal {
if (!SSVStorage.load().token.transfer(to, amount)) {
revert ISSVNetworkCore.TokenTransferFailed();
}
}
function deposit(uint256 amount) internal {
if (!SSVStorage.load().token.transferFrom(msg.sender, address(this), amount)) {
revert ISSVNetworkCore.TokenTransferFailed();
}
}
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
if (account == address(0)) {
return false;
}
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly {
size := extcodesize(account)
}
return size > 0;
}
function setModuleContract(SSVModules moduleId, address moduleAddress) internal {
if (!isContract(moduleAddress)) revert ISSVNetworkCore.TargetModuleDoesNotExistWithData(uint8(moduleId));
SSVStorage.load().ssvContracts[moduleId] = moduleAddress;
emit ModuleUpgraded(moduleId, moduleAddress);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
import "../interfaces/ISSVNetworkCore.sol";
import {ISSVWhitelistingContract} from "../interfaces/external/ISSVWhitelistingContract.sol";
import {StorageData} from "./SSVStorage.sol";
import {StorageProtocol} from "./SSVStorageProtocol.sol";
import {Types64} from "./Types.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
library OperatorLib {
using Types64 for uint64;
function updateSnapshot(ISSVNetworkCore.Operator memory operator) internal view {
uint64 blockDiffFee = (uint32(block.number) - operator.snapshot.block) * operator.fee;
operator.snapshot.index += blockDiffFee;
operator.snapshot.balance += blockDiffFee * operator.validatorCount;
operator.snapshot.block = uint32(block.number);
}
function updateSnapshotSt(ISSVNetworkCore.Operator storage operator) internal {
uint64 blockDiffFee = (uint32(block.number) - operator.snapshot.block) * operator.fee;
operator.snapshot.index += blockDiffFee;
operator.snapshot.balance += blockDiffFee * operator.validatorCount;
operator.snapshot.block = uint32(block.number);
}
function checkOwner(ISSVNetworkCore.Operator memory operator) internal view {
if (operator.snapshot.block == 0) revert ISSVNetworkCore.OperatorDoesNotExist();
if (operator.owner != msg.sender) revert ISSVNetworkCore.CallerNotOwnerWithData(msg.sender, operator.owner);
}
function updateClusterOperatorsOnRegistration(
uint64[] memory operatorIds,
uint32 deltaValidatorCount,
StorageData storage s,
StorageProtocol storage sp
) internal returns (uint64 cumulativeIndex, uint64 cumulativeFee) {
uint256 operatorsLength = operatorIds.length;
uint256 blockIndex;
uint256 lastBlockIndex = ~uint256(0); // Use an invalid block index as the initial value
uint256 currentWhitelistedMask;
for (uint256 i; i < operatorsLength; ++i) {
uint64 operatorId = operatorIds[i];
if (i + 1 < operatorsLength) {
if (operatorId > operatorIds[i + 1]) {
revert ISSVNetworkCore.UnsortedOperatorsList();
} else if (operatorId == operatorIds[i + 1]) {
revert ISSVNetworkCore.OperatorsListNotUnique();
}
}
ISSVNetworkCore.Operator memory operator = s.operators[operatorId];
if (operator.snapshot.block == 0) {
revert ISSVNetworkCore.OperatorDoesNotExist();
}
// check if the pending operator is whitelisted (must be backward compatible)
if (operator.whitelisted) {
// Handle bitmap-based whitelisting
blockIndex = operatorId >> 8;
if (blockIndex != lastBlockIndex) {
currentWhitelistedMask = s.addressWhitelistedForOperators[msg.sender][blockIndex];
lastBlockIndex = blockIndex;
}
// if msg.sender is not whitelisted via bitmap, check for legacy whitelist/whitelisting contract
if (currentWhitelistedMask & (1 << (operatorId & 0xFF)) == 0) {
address whitelistedAddress = s.operatorsWhitelist[operatorId];
if (whitelistedAddress == address(0)) {
// msg.sender is not whitelisted via bitmap or legacy whitelist/whitelisting contract
revert ISSVNetworkCore.CallerNotWhitelistedWithData(operatorId);
}
// Legacy address & whitelisting contract check
if (whitelistedAddress != msg.sender) {
// Check if whitelistedAddress is a valid whitelisting contract and if msg.sender is whitelisted by it
// For non-whitelisting contracts, check if msg.sender is whitelisted (EOAs or generic contracts)
if (
!OperatorLib.isWhitelistingContract(whitelistedAddress) ||
!ISSVWhitelistingContract(whitelistedAddress).isWhitelisted(msg.sender, operatorId)
) {
revert ISSVNetworkCore.CallerNotWhitelistedWithData(operatorId);
}
}
}
}
updateSnapshot(operator);
if ((operator.validatorCount += deltaValidatorCount) > sp.validatorsPerOperatorLimit) {
revert ISSVNetworkCore.ExceedValidatorLimitWithData(operatorId);
}
cumulativeFee += operator.fee;
cumulativeIndex += operator.snapshot.index;
s.operators[operatorId] = operator;
}
}
function updateClusterOperators(
uint64[] memory operatorIds,
bool increaseValidatorCount,
uint32 deltaValidatorCount,
StorageData storage s,
StorageProtocol storage sp
) internal returns (uint64 cumulativeIndex, uint64 cumulativeFee) {
uint256 operatorsLength = operatorIds.length;
for (uint256 i; i < operatorsLength; ++i) {
uint64 operatorId = operatorIds[i];
ISSVNetworkCore.Operator storage operator = s.operators[operatorId];
if (operator.snapshot.block != 0) {
updateSnapshotSt(operator);
if (!increaseValidatorCount) {
operator.validatorCount -= deltaValidatorCount;
} else if ((operator.validatorCount += deltaValidatorCount) > sp.validatorsPerOperatorLimit) {
revert ISSVNetworkCore.ExceedValidatorLimitWithData(operatorId);
}
cumulativeFee += operator.fee;
}
cumulativeIndex += operator.snapshot.index;
}
}
function updateMultipleWhitelists(
address[] calldata whitelistAddresses,
uint64[] calldata operatorIds,
bool registerAddresses,
StorageData storage s
) internal {
uint256 addressesLength = whitelistAddresses.length;
if (addressesLength == 0) revert ISSVNetworkCore.InvalidWhitelistAddressesLength();
checkOperatorsLength(operatorIds);
// create the max number of masks that will be updated
(uint256[] memory masks, uint256 startBlockIndex) = generateBlockMasks(operatorIds, true, s);
uint256 endBlockIndex = startBlockIndex + masks.length;
for (uint256 i; i < addressesLength; ++i) {
address whitelistAddress = whitelistAddresses[i];
checkZeroAddress(whitelistAddress);
// If whitelistAddress is a custom contract, reverts only when registering addresses
if (registerAddresses && isWhitelistingContract(whitelistAddress))
revert ISSVNetworkCore.AddressIsWhitelistingContract(whitelistAddress);
for (uint256 blockIndex = startBlockIndex; blockIndex < endBlockIndex; ++blockIndex) {
// only update storage for updated masks
uint256 mask = masks[blockIndex - startBlockIndex];
if (mask != 0) {
if (registerAddresses) {
s.addressWhitelistedForOperators[whitelistAddress][blockIndex] |= mask;
} else {
s.addressWhitelistedForOperators[whitelistAddress][blockIndex] &= ~mask;
}
}
}
}
}
function generateBlockMasks(
uint64[] calldata operatorIds,
bool checkOperatorsOwnership,
StorageData storage s
) internal view returns (uint256[] memory masks, uint256 startBlockIndex) {
uint256 operatorsLength = operatorIds.length;
startBlockIndex = operatorIds[0] >> 8;
// Create the masks array from startBlockIndex to the last block index
masks = new uint256[]((operatorIds[operatorsLength - 1] >> 8) - startBlockIndex + 1);
uint64 currentOperatorId;
uint64 prevOperatorId;
for (uint256 i; i < operatorsLength; ++i) {
currentOperatorId = operatorIds[i];
if (checkOperatorsOwnership) {
checkOwner(s.operators[currentOperatorId]);
}
if (i > 0 && currentOperatorId <= prevOperatorId) {
if (currentOperatorId == prevOperatorId) {
revert ISSVNetworkCore.OperatorsListNotUnique();
}
revert ISSVNetworkCore.UnsortedOperatorsList();
}
(uint256 blockIndex, uint256 bitPosition) = getBitmapIndexes(currentOperatorId);
masks[blockIndex - startBlockIndex] |= (1 << bitPosition);
prevOperatorId = currentOperatorId;
}
}
function updatePrivacyStatus(uint64[] calldata operatorIds, bool setPrivate, StorageData storage s) internal {
uint256 operatorsLength = checkOperatorsLength(operatorIds);
ISSVNetworkCore.Operator storage operator;
for (uint256 i; i < operatorsLength; ++i) {
uint64 operatorId = operatorIds[i];
operator = s.operators[operatorId];
checkOwner(operator);
operator.whitelisted = setPrivate;
}
}
function getBitmapIndexes(uint64 operatorId) internal pure returns (uint256 blockIndex, uint256 bitPosition) {
blockIndex = operatorId >> 8; // Equivalent to operatorId / 256
bitPosition = operatorId & 0xFF; // Equivalent to operatorId % 256
}
function checkZeroAddress(address whitelistAddress) internal pure {
if (whitelistAddress == address(0)) revert ISSVNetworkCore.ZeroAddressNotAllowed();
}
function checkOperatorsLength(uint64[] calldata operatorIds) internal pure returns (uint256 operatorsLength) {
operatorsLength = operatorIds.length;
if (operatorsLength == 0) revert ISSVNetworkCore.InvalidOperatorIdsLength();
}
function isWhitelistingContract(address whitelistingContract) internal view returns (bool) {
return ERC165Checker.supportsInterface(whitelistingContract, type(ISSVWhitelistingContract).interfaceId);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
import "../interfaces/ISSVNetworkCore.sol";
import {Types256} from "./Types.sol";
import {StorageProtocol} from "./SSVStorageProtocol.sol";
library ProtocolLib {
using Types256 for uint256;
/******************************/
/* Network internal functions */
/******************************/
function currentNetworkFeeIndex(StorageProtocol storage sp) internal view returns (uint64) {
return sp.networkFeeIndex + uint64(block.number - sp.networkFeeIndexBlockNumber) * sp.networkFee;
}
function updateNetworkFee(StorageProtocol storage sp, uint256 fee) internal {
updateDAOEarnings(sp);
sp.networkFeeIndex = currentNetworkFeeIndex(sp);
sp.networkFeeIndexBlockNumber = uint32(block.number);
sp.networkFee = fee.shrink();
}
/**************************/
/* DAO internal functions */
/**************************/
function updateDAOEarnings(StorageProtocol storage sp) internal {
sp.daoBalance = networkTotalEarnings(sp);
sp.daoIndexBlockNumber = uint32(block.number);
}
function networkTotalEarnings(StorageProtocol storage sp) internal view returns (uint64) {
return sp.daoBalance + (uint64(block.number) - sp.daoIndexBlockNumber) * sp.networkFee * sp.daoValidatorCount;
}
function updateDAO(StorageProtocol storage sp, bool increaseValidatorCount, uint32 deltaValidatorCount) internal {
updateDAOEarnings(sp);
if (!increaseValidatorCount) {
sp.daoValidatorCount -= deltaValidatorCount;
} else if ((sp.daoValidatorCount += deltaValidatorCount) > type(uint32).max) {
revert ISSVNetworkCore.MaxValueExceeded();
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
import "../interfaces/ISSVNetworkCore.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
enum SSVModules {
SSV_OPERATORS,
SSV_CLUSTERS,
SSV_DAO,
SSV_VIEWS,
SSV_OPERATORS_WHITELIST
}
/// @title SSV Network Storage Data
/// @notice Represents all operational state required by the SSV Network
struct StorageData {
/// @notice Maps each validator's public key to its hashed representation of: operator Ids used by the validator and active / inactive flag (uses LSB)
mapping(bytes32 => bytes32) validatorPKs;
/// @notice Maps each cluster's bytes32 identifier to its hashed representation of ISSVNetworkCore.Cluster
mapping(bytes32 => bytes32) clusters;
/// @notice Maps each operator's public key to its corresponding ID
mapping(bytes32 => uint64) operatorsPKs;
/// @notice Maps each SSVModules' module to its corresponding contract address
mapping(SSVModules => address) ssvContracts;
/// @notice Operators' whitelist: Maps each operator's ID to a whitelisting contract
mapping(uint64 => address) operatorsWhitelist;
/// @notice Maps each operator's ID to its corresponding operator fee change request data
mapping(uint64 => ISSVNetworkCore.OperatorFeeChangeRequest) operatorFeeChangeRequests;
/// @notice Maps each operator's ID to its corresponding operator data
mapping(uint64 => ISSVNetworkCore.Operator) operators;
/// @notice The SSV token used within the network (fees, rewards)
IERC20 token;
/// @notice Counter keeping track of the last Operator ID issued
Counters.Counter lastOperatorId;
/// @notice Operators' whitelist: Maps each whitelisted address to a list of operators
/// @notice that are whitelisted for that address using bitmaps
/// @dev The nested mapping's key represents a uint256 slot to handle more than 256 operators per address
mapping(address => mapping(uint256 => uint256)) addressWhitelistedForOperators;
}
library SSVStorage {
uint256 private constant SSV_STORAGE_POSITION = uint256(keccak256("ssv.network.storage.main")) - 1;
function load() internal pure returns (StorageData storage sd) {
uint256 position = SSV_STORAGE_POSITION;
assembly {
sd.slot := position
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
/// @title SSV Network Storage Protocol
/// @notice Represents the operational settings and parameters required by the SSV Network
struct StorageProtocol {
/// @notice The block number when the network fee index was last updated
uint32 networkFeeIndexBlockNumber;
/// @notice The count of validators governed by the DAO
uint32 daoValidatorCount;
/// @notice The block number when the DAO index was last updated
uint32 daoIndexBlockNumber;
/// @notice The maximum limit of validators per operator
uint32 validatorsPerOperatorLimit;
/// @notice The current network fee value
uint64 networkFee;
/// @notice The current network fee index value
uint64 networkFeeIndex;
/// @notice The current balance of the DAO
uint64 daoBalance;
/// @notice The minimum number of blocks before a liquidation event can be triggered
uint64 minimumBlocksBeforeLiquidation;
/// @notice The minimum collateral required for liquidation
uint64 minimumLiquidationCollateral;
/// @notice The period in which an operator can declare a fee change
uint64 declareOperatorFeePeriod;
/// @notice The period in which an operator fee change can be executed
uint64 executeOperatorFeePeriod;
/// @notice The maximum increase in operator fee that is allowed (percentage)
uint64 operatorMaxFeeIncrease;
/// @notice The maximum value in operator fee that is allowed (SSV)
uint64 operatorMaxFee;
}
library SSVStorageProtocol {
uint256 private constant SSV_STORAGE_POSITION = uint256(keccak256("ssv.network.storage.protocol")) - 1;
function load() internal pure returns (StorageProtocol storage sd) {
uint256 position = SSV_STORAGE_POSITION;
assembly {
sd.slot := position
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
uint256 constant DEDUCTED_DIGITS = 10_000_000;
library Types64 {
function expand(uint64 value) internal pure returns (uint256) {
return value * DEDUCTED_DIGITS;
}
}
library Types256 {
function shrink(uint256 value) internal pure returns (uint64) {
require(value < (2 ** 64 * DEDUCTED_DIGITS), "Max value exceeded");
return uint64(shrinkable(value) / DEDUCTED_DIGITS);
}
function shrinkable(uint256 value) internal pure returns (uint256) {
require(value % DEDUCTED_DIGITS == 0, "Max precision exceeded");
return value;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
import "../interfaces/ISSVNetworkCore.sol";
import {StorageData} from "./SSVStorage.sol";
library ValidatorLib {
uint64 private constant MIN_OPERATORS_LENGTH = 4;
uint64 private constant MAX_OPERATORS_LENGTH = 13;
uint64 private constant MODULO_OPERATORS_LENGTH = 3;
uint64 private constant PUBLIC_KEY_LENGTH = 48;
function validateOperatorsLength(uint64[] memory operatorIds) internal pure {
uint256 operatorsLength = operatorIds.length;
if (
operatorsLength < MIN_OPERATORS_LENGTH ||
operatorsLength > MAX_OPERATORS_LENGTH ||
operatorsLength % MODULO_OPERATORS_LENGTH != 1
) {
revert ISSVNetworkCore.InvalidOperatorIdsLength();
}
}
function registerPublicKey(bytes memory publicKey, uint64[] memory operatorIds, StorageData storage s) internal {
if (publicKey.length != PUBLIC_KEY_LENGTH) {
revert ISSVNetworkCore.InvalidPublicKeyLength();
}
bytes32 hashedPk = keccak256(abi.encodePacked(publicKey, msg.sender));
if (s.validatorPKs[hashedPk] != bytes32(0)) {
revert ISSVNetworkCore.ValidatorAlreadyExistsWithData(publicKey);
}
s.validatorPKs[hashedPk] = bytes32(uint256(keccak256(abi.encodePacked(operatorIds))) | uint256(0x01)); // set LSB to 1
}
function hashOperatorIds(uint64[] memory operatorIds) internal pure returns (bytes32) {
bytes32 mask = ~bytes32(uint256(1)); // All bits set to 1 except LSB
return keccak256(abi.encodePacked(operatorIds)) & mask; // Clear LSB of provided operator ids
}
function validateCorrectState(bytes32 validatorData, bytes32 hashedOperatorIds) internal pure returns (bool) {
// All bits set to 1 except LSB
// Clear LSB of stored validator data and compare
return (validatorData & ~bytes32(uint256(1))) == hashedOperatorIds;
}
}{
"optimizer": {
"enabled": true,
"runs": 10000
},
"evmVersion": "cancun",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"AddressIsWhitelistingContract","type":"error"},{"inputs":[],"name":"ApprovalNotWithinTimeframe","type":"error"},{"inputs":[],"name":"CallerNotOwner","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"CallerNotOwnerWithData","type":"error"},{"inputs":[],"name":"CallerNotWhitelisted","type":"error"},{"inputs":[{"internalType":"uint64","name":"operatorId","type":"uint64"}],"name":"CallerNotWhitelistedWithData","type":"error"},{"inputs":[],"name":"ClusterAlreadyEnabled","type":"error"},{"inputs":[],"name":"ClusterDoesNotExists","type":"error"},{"inputs":[],"name":"ClusterIsLiquidated","type":"error"},{"inputs":[],"name":"ClusterNotLiquidatable","type":"error"},{"inputs":[],"name":"EmptyPublicKeysList","type":"error"},{"inputs":[{"internalType":"uint64","name":"operatorId","type":"uint64"}],"name":"ExceedValidatorLimit","type":"error"},{"inputs":[{"internalType":"uint64","name":"operatorId","type":"uint64"}],"name":"ExceedValidatorLimitWithData","type":"error"},{"inputs":[],"name":"FeeExceedsIncreaseLimit","type":"error"},{"inputs":[],"name":"FeeIncreaseNotAllowed","type":"error"},{"inputs":[],"name":"FeeTooHigh","type":"error"},{"inputs":[],"name":"FeeTooLow","type":"error"},{"inputs":[],"name":"IncorrectClusterState","type":"error"},{"inputs":[],"name":"IncorrectValidatorState","type":"error"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"}],"name":"IncorrectValidatorStateWithData","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidContractAddress","type":"error"},{"inputs":[],"name":"InvalidOperatorIdsLength","type":"error"},{"inputs":[],"name":"InvalidPublicKeyLength","type":"error"},{"inputs":[],"name":"InvalidWhitelistAddressesLength","type":"error"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"InvalidWhitelistingContract","type":"error"},{"inputs":[],"name":"MaxValueExceeded","type":"error"},{"inputs":[],"name":"NewBlockPeriodIsBelowMinimum","type":"error"},{"inputs":[],"name":"NoFeeDeclared","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"OperatorAlreadyExists","type":"error"},{"inputs":[],"name":"OperatorDoesNotExist","type":"error"},{"inputs":[],"name":"OperatorsListNotUnique","type":"error"},{"inputs":[],"name":"PublicKeysSharesLengthMismatch","type":"error"},{"inputs":[],"name":"SameFeeChangeNotAllowed","type":"error"},{"inputs":[],"name":"TargetModuleDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint8","name":"moduleId","type":"uint8"}],"name":"TargetModuleDoesNotExistWithData","type":"error"},{"inputs":[],"name":"TokenTransferFailed","type":"error"},{"inputs":[],"name":"UnsortedOperatorsList","type":"error"},{"inputs":[],"name":"ValidatorAlreadyExists","type":"error"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"}],"name":"ValidatorAlreadyExistsWithData","type":"error"},{"inputs":[],"name":"ValidatorDoesNotExist","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ClusterDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ClusterLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ClusterReactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ClusterWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"bytes","name":"publicKey","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"shares","type":"bytes"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ValidatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"bytes","name":"publicKey","type":"bytes"}],"name":"ValidatorExited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"bytes","name":"publicKey","type":"bytes"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ValidatorRemoved","type":"event"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"bulkExitValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"bytes[]","name":"sharesData","type":"bytes[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"bulkRegisterValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"bulkRemoveValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"clusterOwner","type":"address"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"exitValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"clusterOwner","type":"address"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"reactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"bytes","name":"sharesData","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"registerValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"removeValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561000f575f80fd5b506137788061001d5f395ff3fe608060405234801561000f575f80fd5b50600436106100b9575f3560e01c80635aed114211610072578063686e682c11610058578063686e682c14610144578063bc26e7e514610157578063bf0f2fb21461016a575f80fd5b80635aed11421461011e5780635fec6dd014610131575f80fd5b806322f18bf5116100a257806322f18bf5146100e557806332afd02f146100f85780633877322b1461010b575f80fd5b806306e8fb9c146100bd57806312b3fc19146100d2575b5f80fd5b6100d06100cb366004612b5c565b61017d565b005b6100d06100e0366004612c04565b610283565b6100d06100f3366004612cbb565b610459565b6100d0610106366004612e2a565b61063d565b6100d0610119366004612e91565b61081c565b6100d061012c366004612ec7565b61095a565b6100d061013f366004612efd565b610b84565b6100d0610152366004612efd565b610da7565b6100d0610165366004612f6f565b611081565b6100d0610178366004612fda565b611164565b5f610186611372565b90505f6101916113a5565b905061019c876113d2565b6101de89898080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508b92508691506114349050565b5f6101ea848985611520565b905084846080018181516101fe919061305e565b90525061021084898360018787611645565b841561021f5761021f85611732565b3373ffffffffffffffffffffffffffffffffffffffff167f48a3ea0796746043948f6341d17ff8200937b99262a0b48c2663b951ed7114e5898c8c8b8b8a60405161026f969594939291906130de565b60405180910390a250505050505050505050565b5f61028c611372565b90505f61029b83338685611812565b90505f6102a7856118d5565b90505f8787336040516020016102bf9392919061316e565b60408051601f1981840301815291815281516020928301205f818152928790529120549091508061031c576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe811683146103845788886040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b60405180910390fd5b5f828152602086905260408120556060860151156103dd575f6103a56113a5565b90505f6103b6895f60018a8661192b565b5090506103ce816103c684611ac6565b8a9190611b35565b6103da825f6001611b5a565b50505b855186906103ea906131c2565b63ffffffff1690526103fb86611c31565b5f85815260018701602052604090819020919091555133907fccf4370403e5fbbde0cd3f13426479dcd8a5916b05db424b7a2c04978cf8ce6e90610446908a908d908d908c906131ff565b60405180910390a2505050505050505050565b85515f819003610495576040517fdf83e67900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8084146104ce576040517f9ad467b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6104d7611372565b90505f6104e26113a5565b90506104ed886113d2565b5f5b838110156105235761051b8a828151811061050c5761050c613277565b60200260200101518a85611434565b6001016104ef565b505f610530858a85611520565b90508585608001818151610544919061305e565b905250610555858a83878787611645565b85156105645761056486611732565b5f5b84811015610630575f8b828151811061058157610581613277565b602002602001015190505f8a8a8481811061059e5761059e613277565b90506020028101906105b091906132a4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505060405192935033927f48a3ea0796746043948f6341d17ff8200937b99262a0b48c2663b951ed7114e5925061061e91508f90869086908e90613352565b60405180910390a25050600101610566565b5050505050505050505050565b5f839003610677576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6106b38383808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506118d592505050565b90505f5b84811015610814576107506106ca611372565b5f8888858181106106dd576106dd613277565b90506020028101906106ef91906132a4565b336040516020016107029392919061316e565b6040516020818303038152906040528051906020012081526020019081526020015f2054837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe919091161490565b6107ac5785858281811061076657610766613277565b905060200281019061077891906132a4565b6040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b337fb4b20ffb2eb1f020be3df600b2287914f50c07003526d3a9d89a9dd12351828c85858989868181106107e2576107e2613277565b90506020028101906107f491906132a4565b604051610804949392919061341b565b60405180910390a26001016106b7565b505050505050565b6108c6610827611372565b6040515f9061083e9088908890339060200161316e565b6040516020818303038152906040528051906020012081526020019081526020015f205461089d8484808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506118d592505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe919091161490565b6109005783836040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b3373ffffffffffffffffffffffffffffffffffffffff167fb4b20ffb2eb1f020be3df600b2287914f50c07003526d3a9d89a9dd12351828c8383878760405161094c949392919061341b565b60405180910390a250505050565b825f819003610995576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61099e611372565b90505f6109ad84338785611812565b90505f6109b9866118d5565b90505f805f805b87811015610a85578b8b828181106109da576109da613277565b90506020028101906109ec91906132a4565b336040516020016109ff9392919061316e565b60408051808303601f1901815291815281516020928301205f818152928a905291205490945092507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83168514610a62578b8b8281811061076657610766613277565b5f8481526020889052604081205581610a7a81613441565b9250506001016109c0565b50876060015115610acf575f610a996113a5565b90505f610aa98b5f858b8661192b565b509050610ac181610ab984611ac6565b8c9190611b35565b610acc825f85611b5a565b50505b80885f01818151610ae09190613463565b63ffffffff16905250610af288611c31565b5f8681526001880160205260408120919091555b87811015610b7657337fccf4370403e5fbbde0cd3f13426479dcd8a5916b05db424b7a2c04978cf8ce6e8b8e8e85818110610b4357610b43613277565b9050602002810190610b5591906132a4565b8d604051610b6694939291906131ff565b60405180910390a2600101610b06565b505050505050505050505050565b5f610b8d611372565b90505f610bd1338787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b9050826060015115610c0f576040517f3babafd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610c186113a5565b90505f80610c5e8989808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250508951600192509050888761192b565b915091508686608001818151610c74919061305e565b9052506001606087015267ffffffffffffffff82166040870152610c9783611ac6565b67ffffffffffffffff1660208701528551610cb6908490600190611b5a565b82546001840154610cfe918891849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b15610d35576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d3e86611c31565b5f8581526001870160205260409020558615610d5d57610d5d87611732565b3373ffffffffffffffffffffffffffffffffffffffff167fc803f8c01343fcdaf32068f4c283951623ef2b3fa0c547551931356f456b68598a8a8960405161044693929190613487565b5f610db0611372565b90505f610df4338787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b9050610dff83611d71565b5f610e086113a5565b90505f846060015115610f10575f87815b81811015610ef7575f610e2a611372565b6006015f8d8d85818110610e4057610e40613277565b9050602002016020810190610e5591906134ec565b67ffffffffffffffff908116825260208201929092526040015f2080546002820154919350640100000000900490911690610e969063ffffffff1643613505565b610ea09190613526565b6002820154610ec19190640100000000900467ffffffffffffffff16613552565b610ecb9085613552565b8154909450610eec90640100000000900467ffffffffffffffff1686613552565b945050600101610e19565b5050610f0e81610f0685611ac6565b889190611b35565b505b8585608001511015610f4e576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8585608001818151610f609190613573565b90525060608501518015610f7a5750845163ffffffff1615155b8015610fc9575081546001830154610fc9918791849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b15611000576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61100985611c31565b5f8481526001860160205260409020556110233387611dac565b3373ffffffffffffffffffffffffffffffffffffffff167f39d1320bbda24947e77f3560661323384aa0a1cb9d5e040e617e5cbf50b6dbe08989898960405161106f9493929190613586565b60405180910390a25050505050505050565b5f61108a611372565b90505f6110ce878787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b905083836080018181516110e2919061305e565b9052506110ee83611c31565b5f82815260018401602052604090205561110784611732565b8673ffffffffffffffffffffffffffffffffffffffff167f2bac1912f2481d12f0df08647c06bee174967c62d3a03cbc078eb215dc1bd9a2878787876040516111539493929190613586565b60405180910390a250505050505050565b5f61116d611372565b90505f6111b1868686808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b90506111bc83611d71565b5f6111c56113a5565b90505f8061120a8888808060200260200160405190810160405280939291908181526020018383602002808284375f9201829052508b5190935091508990508761192b565b915091506112238261121b85611ac6565b889190611e85565b5f73ffffffffffffffffffffffffffffffffffffffff8a16331480159061128f57508354600185015461128d918991859167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b155b156112c6576040517f60300a8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86516112d59085905f90611b5a565b6080870151156112eb57506080860180515f9091525b5f6040880181905260208801819052606088015261130887611c31565b5f8681526001880160205260409020558015611328576113283382611dac565b8973ffffffffffffffffffffffffffffffffffffffff167f1fce24c373e07f89214e9187598635036111dbb363e99f4ce498488cdc66e6888a8a8a60405161026f93929190613487565b5f8061139f60017fd56c4f4aab8ca22f9fde432777379f436593c6027698a6995e2daea890bed105613573565b92915050565b5f8061139f60017f0f1d85405047bdb6b0a60e27531f52a1f7a948613527b9b83a7552558207aa16613573565b805160048110806113e35750600d81115b806113f957506113f46003826135f1565b600114155b15611430576040517f3818622400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b825160301461146f576040517f637297a400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8333604051602001611483929190613629565b60408051601f1981840301815291815281516020928301205f81815292859052912054909150156114e257836040517f388e799900000000000000000000000000000000000000000000000000000000815260040161037b9190613672565b6001836040516020016114f591906136b2565b60408051601f1981840301815291815281516020928301205f94855294909152909120911790555050565b5f33836040516020016115349291906136bd565b60408051601f1981840301815291815281516020928301205f81815260018601909352912054909150806115f357845163ffffffff161515806115845750602085015167ffffffffffffffff1615155b8061159c5750604085015167ffffffffffffffff1615155b806115aa5750608085015115155b806115b757508460600151155b156115ee576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d565b6115fc85611c31565b8114611634576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d85611d71565b509392505050565b5f8061165387868686611f32565b9150915061166a888361166586611ac6565b611b35565b61167683600187611b5a565b84885f0181815161168791906136f3565b63ffffffff16905250825460018401546116d8918a91849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b1561170f576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61171888611c31565b5f9687526001909401602052505060409093205550505050565b61173a611372565b600701546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810183905273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd906064016020604051808303815f875af11580156117b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117d99190613710565b61180f576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f83836040516020016118269291906136bd565b6040516020818303038152906040528051906020012090505f61184886611c31565b5f83815260018501602052604090205490915080611892576040517f185e2b1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8181146118cb576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b6040515f907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90819061190c9085906020016136b2565b6040516020818303038152906040528051906020012016915050919050565b84515f908190815b81811015611aba575f89828151811061194e5761194e613277565b60209081029190910181015167ffffffffffffffff81165f90815260068a01909252604090912060028101549192509063ffffffff1615611a8a5761199281612527565b896119d3578054899082905f906119b090849063ffffffff16613463565b92506101000a81548163ffffffff021916908363ffffffff160217905550611a69565b8654815463ffffffff6c010000000000000000000000009092048216918b9184915f91611a02918591166136f3565b92506101000a81548163ffffffff021916908363ffffffff160217905563ffffffff161115611a69576040517f639f585100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8316600482015260240161037b565b8054611a8790640100000000900467ffffffffffffffff1686613552565b94505b6002810154611aab90640100000000900467ffffffffffffffff1687613552565b95505050806001019050611933565b50509550959350505050565b80545f9067ffffffffffffffff70010000000000000000000000000000000082041690611af99063ffffffff1643613573565b611b039190613526565b825461139f91907801000000000000000000000000000000000000000000000000900467ffffffffffffffff16613552565b611b40838383611e85565b67ffffffffffffffff918216604084015216602090910152565b611b638361264b565b81611bad57825481908490600490611b8a908490640100000000900463ffffffff16613463565b92506101000a81548163ffffffff021916908363ffffffff160217905550505050565b825463ffffffff9082908590600490611bd1908490640100000000900486166136f3565b92506101000a81548163ffffffff021916908363ffffffff160217905563ffffffff161115611c2c576040517f91aa301700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b80516020808301516040808501516080860151606087015192515f96611ccf96909594910160e09590951b7fffffffff0000000000000000000000000000000000000000000000000000000016855260c093841b7fffffffffffffffff00000000000000000000000000000000000000000000000090811660048701529290931b909116600c8401526014830152151560f81b603482015260350190565b604051602081830303815290604052805190602001209050919050565b84515f9063ffffffff1615611d6857611d0e8267ffffffffffffffff166126ca565b86608001511015611d2157506001611d68565b85515f9063ffffffff16611d358688613552565b611d3f9086613526565b611d499190613526565b9050611d5e8167ffffffffffffffff166126ca565b8760800151109150505b95945050505050565b806060015161180f576040517f95a0cf3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611db4611372565b600701546040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490529091169063a9059cbb906044016020604051808303815f875af1158015611e2b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e4f9190613710565b611430576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401515f9163ffffffff1690611e9f9084613505565b611ea99190613526565b90505f81855f015163ffffffff16866040015186611ec79190613505565b611ed19190613526565b611edb9190613552565b90508460800151611ef58267ffffffffffffffff166126ca565b11611f2157611f0d8167ffffffffffffffff166126ca565b8560800151611f1c9190613573565b611f23565b5f5b60809095019490945250505050565b83515f908190817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81805b84811015612519575f8b8281518110611f7857611f78613277565b6020026020010151905085826001611f90919061305e565b1015612077578b611fa283600161305e565b81518110611fb257611fb2613277565b602002602001015167ffffffffffffffff168167ffffffffffffffff161115612007576040517fdd020e2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8b61201383600161305e565b8151811061202357612023613277565b602002602001015167ffffffffffffffff168167ffffffffffffffff1603612077576040517fa5a1ff5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8082165f90815260068c0160209081526040808320815160a081018352815463ffffffff808216835264010000000080830489168488015273ffffffffffffffffffffffffffffffffffffffff6c01000000000000000000000000938490041684870152600185015460ff1615156060808601919091528651908101875260029095015480831686529081048916968501969096529404909516918101919091526080840181905251169003612162576040517f961e3e8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060600151156123385766ffffffffffffff600883901c1695508486146121a557335f90815260098c016020908152604080832089845290915290205486955093505b600160ff83161b84165f036123385767ffffffffffffffff82165f90815260048c01602052604090205473ffffffffffffffffffffffffffffffffffffffff1680612228576040517fb7f529fe00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8416600482015260240161037b565b73ffffffffffffffffffffffffffffffffffffffff811633146123365761224e816126e2565b15806122f357506040517f830639ac00000000000000000000000000000000000000000000000000000000815233600482015267ffffffffffffffff8416602482015273ffffffffffffffffffffffffffffffffffffffff82169063830639ac90604401602060405180830381865afa1580156122cd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f19190613710565b155b15612336576040517fb7f529fe00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8416600482015260240161037b565b505b6123418161270d565b895481516c0100000000000000000000000090910463ffffffff16908d90839061236c9083906136f3565b63ffffffff1690819052919091111590506123bf576040517f639f585100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8316600482015260240161037b565b60208101516123ce9089613552565b9750806080015160200151896123e49190613552565b67ffffffffffffffff9283165f90815260068d01602090815260409182902084518154868401518786015163ffffffff9384167fffffffffffffffffffffffffffffffffffffffff00000000000000000000000093841617640100000000928b168302176bffffffffffffffffffffffff166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90921682021785556060890151600180870180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001692151592909217909155608090990151805160029096018054978201519190980151959094169590921694909417918816909302177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff169516029390931790925590975001611f5d565b505050505094509492505050565b805460028201545f91640100000000900467ffffffffffffffff16906125539063ffffffff1643613463565b63ffffffff166125639190613526565b600283018054919250829160049061258e908490640100000000900467ffffffffffffffff16613552565b825467ffffffffffffffff9182166101009390930a92830291909202199091161790555081546125c49063ffffffff1682613526565b600283018054600c906125f29084906c01000000000000000000000000900467ffffffffffffffff16613552565b82546101009290920a67ffffffffffffffff818102199093169190921691909102179055505060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000164363ffffffff16179055565b6126548161279e565b6001820180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff9290921691909117905580547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff16680100000000000000004363ffffffff1602179055565b5f61139f6298968067ffffffffffffffff841661372b565b5f61139f827f830639ac00000000000000000000000000000000000000000000000000000000612815565b60208101516080820151515f91906127259043613463565b63ffffffff166127359190613526565b905080826080015160200181815161274d9190613552565b67ffffffffffffffff16905250815161276c9063ffffffff1682613526565b82608001516040018181516127819190613552565b67ffffffffffffffff16905250506080015163ffffffff43169052565b80545f9063ffffffff640100000000820481169167ffffffffffffffff700100000000000000000000000000000000820416916127e8916801000000000000000090041643613505565b6127f29190613526565b6127fc9190613526565b600183015461139f919067ffffffffffffffff16613552565b5f61281f83612837565b80156128305750612830838361289a565b9392505050565b5f612862827f01ffc9a70000000000000000000000000000000000000000000000000000000061289a565b801561139f5750612893827fffffffff0000000000000000000000000000000000000000000000000000000061289a565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a70000000000000000000000000000000000000000000000000000000017815282515f9392849283928392918391908a617530fa92503d91505f519050828015612950575060208210155b801561295b57505f81115b979650505050505050565b5f8083601f840112612976575f80fd5b50813567ffffffffffffffff81111561298d575f80fd5b6020830191508360208285010111156129a4575f80fd5b9250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612a0157612a016129ab565b604052919050565b5f67ffffffffffffffff821115612a2257612a226129ab565b5060051b60200190565b803567ffffffffffffffff81168114612a43575f80fd5b919050565b5f82601f830112612a57575f80fd5b81356020612a6c612a6783612a09565b6129d8565b8083825260208201915060208460051b870101935086841115612a8d575f80fd5b602086015b84811015612ab057612aa381612a2c565b8352918301918301612a92565b509695505050505050565b801515811461180f575f80fd5b5f60a08284031215612ad8575f80fd5b60405160a0810181811067ffffffffffffffff82111715612afb57612afb6129ab565b604052905080823563ffffffff81168114612b14575f80fd5b8152612b2260208401612a2c565b6020820152612b3360408401612a2c565b60408201526060830135612b4681612abb565b6060820152608092830135920191909152919050565b5f805f805f805f610120888a031215612b73575f80fd5b873567ffffffffffffffff80821115612b8a575f80fd5b612b968b838c01612966565b909950975060208a0135915080821115612bae575f80fd5b612bba8b838c01612a48565b965060408a0135915080821115612bcf575f80fd5b50612bdc8a828b01612966565b90955093505060608801359150612bf68960808a01612ac8565b905092959891949750929550565b5f805f8060e08587031215612c17575f80fd5b843567ffffffffffffffff80821115612c2e575f80fd5b612c3a88838901612966565b90965094506020870135915080821115612c52575f80fd5b50612c5f87828801612a48565b925050612c6f8660408701612ac8565b905092959194509250565b5f8083601f840112612c8a575f80fd5b50813567ffffffffffffffff811115612ca1575f80fd5b6020830191508360208260051b85010111156129a4575f80fd5b5f805f805f806101208789031215612cd1575f80fd5b67ffffffffffffffff8088351115612ce7575f80fd5b8735880189601f820112612cf9575f80fd5b612d06612a678235612a09565b81358082526020808301929160051b8401018c1015612d23575f80fd5b602083015b6020843560051b850101811015612dbd578481351115612d46575f80fd5b803584018d603f820112612d58575f80fd5b602081013586811115612d6d57612d6d6129ab565b612d806020601f19601f840116016129d8565b8181528f6040838501011115612d94575f80fd5b816040840160208301375f60208383010152808652505050602083019250602081019050612d28565b50985050506020880135811015612dd2575f80fd5b612de28960208a01358a01612a48565b95508060408901351115612df4575f80fd5b50612e058860408901358901612c7a565b909450925060608701359150612e1e8860808901612ac8565b90509295509295509295565b5f805f8060408587031215612e3d575f80fd5b843567ffffffffffffffff80821115612e54575f80fd5b612e6088838901612c7a565b90965094506020870135915080821115612e78575f80fd5b50612e8587828801612c7a565b95989497509550505050565b5f805f8060408587031215612ea4575f80fd5b843567ffffffffffffffff80821115612ebb575f80fd5b612e6088838901612966565b5f805f8060e08587031215612eda575f80fd5b843567ffffffffffffffff80821115612ef1575f80fd5b612c3a88838901612c7a565b5f805f8060e08587031215612f10575f80fd5b843567ffffffffffffffff811115612f26575f80fd5b612f3287828801612c7a565b90955093505060208501359150612c6f8660408701612ac8565b803573ffffffffffffffffffffffffffffffffffffffff81168114612a43575f80fd5b5f805f805f6101008688031215612f84575f80fd5b612f8d86612f4c565b9450602086013567ffffffffffffffff811115612fa8575f80fd5b612fb488828901612c7a565b90955093505060408601359150612fce8760608801612ac8565b90509295509295909350565b5f805f8060e08587031215612fed575f80fd5b612ff685612f4c565b9350602085013567ffffffffffffffff811115613011575f80fd5b61301d87828801612c7a565b9094509250612c6f90508660408701612ac8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082018082111561139f5761139f613031565b5f815180845260208085019450602084015f5b838110156130aa57815167ffffffffffffffff1687529582019590820190600101613084565b509495945050505050565b81835281816020850137505f602082840101525f6020601f19601f840116840101905092915050565b5f6101008083526130f18184018a613071565b9050828103602084015261310681888a6130b5565b9050828103604084015261311b8186886130b5565b91505061295b606083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b8284823760609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169101908152601401919050565b602081525f6131ba6020830184866130b5565b949350505050565b5f63ffffffff8216806131d7576131d7613031565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60e081525f61321160e0830187613071565b82810360208401526132248186886130b5565b915050611d68604083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126132d7575f80fd5b83018035915067ffffffffffffffff8211156132f1575f80fd5b6020019150368190038213156129a4575f80fd5b5f5b8381101561331f578181015183820152602001613307565b50505f910152565b5f815180845261333e816020860160208601613305565b601f01601f19169290920160200192915050565b5f61010080835261336581840188613071565b905082810360208401526133798187613327565b9050828103604084015261338d8186613327565b915050611d68606083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b8183525f60208085019450825f5b858110156130aa5767ffffffffffffffff61340883612a2c565b16875295820195908201906001016133ee565b604081525f61342e6040830186886133e0565b828103602084015261295b8185876130b5565b5f63ffffffff80831681810361345957613459613031565b6001019392505050565b63ffffffff82811682821603908082111561348057613480613031565b5092915050565b60c081525f61349a60c0830185876133e0565b90506131ba602083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b5f602082840312156134fc575f80fd5b61283082612a2c565b67ffffffffffffffff82811682821603908082111561348057613480613031565b67ffffffffffffffff81811683821602808216919082811461354a5761354a613031565b505092915050565b67ffffffffffffffff81811683821601908082111561348057613480613031565b8181038181111561139f5761139f613031565b60e081525f61359960e0830186886133e0565b9050836020830152611d68604083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b5f82613624577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500690565b5f835161363a818460208801613305565b60609390931b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169190920190815260140192915050565b602081525f6128306020830184613327565b80515f9060208084018383156130aa57815167ffffffffffffffff1687529582019590820190600101613084565b5f6128308284613684565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008360601b1681525f6131ba6014830184613684565b63ffffffff81811683821601908082111561348057613480613031565b5f60208284031215613720575f80fd5b815161283081612abb565b808202811582820484141761139f5761139f61303156fea26469706673582212201e854e2755e24f7c4b395af307c15de1c046c65db1bd09ae205a35d9392aec7e64736f6c63430008180033
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106100b9575f3560e01c80635aed114211610072578063686e682c11610058578063686e682c14610144578063bc26e7e514610157578063bf0f2fb21461016a575f80fd5b80635aed11421461011e5780635fec6dd014610131575f80fd5b806322f18bf5116100a257806322f18bf5146100e557806332afd02f146100f85780633877322b1461010b575f80fd5b806306e8fb9c146100bd57806312b3fc19146100d2575b5f80fd5b6100d06100cb366004612b5c565b61017d565b005b6100d06100e0366004612c04565b610283565b6100d06100f3366004612cbb565b610459565b6100d0610106366004612e2a565b61063d565b6100d0610119366004612e91565b61081c565b6100d061012c366004612ec7565b61095a565b6100d061013f366004612efd565b610b84565b6100d0610152366004612efd565b610da7565b6100d0610165366004612f6f565b611081565b6100d0610178366004612fda565b611164565b5f610186611372565b90505f6101916113a5565b905061019c876113d2565b6101de89898080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508b92508691506114349050565b5f6101ea848985611520565b905084846080018181516101fe919061305e565b90525061021084898360018787611645565b841561021f5761021f85611732565b3373ffffffffffffffffffffffffffffffffffffffff167f48a3ea0796746043948f6341d17ff8200937b99262a0b48c2663b951ed7114e5898c8c8b8b8a60405161026f969594939291906130de565b60405180910390a250505050505050505050565b5f61028c611372565b90505f61029b83338685611812565b90505f6102a7856118d5565b90505f8787336040516020016102bf9392919061316e565b60408051601f1981840301815291815281516020928301205f818152928790529120549091508061031c576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe811683146103845788886040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b60405180910390fd5b5f828152602086905260408120556060860151156103dd575f6103a56113a5565b90505f6103b6895f60018a8661192b565b5090506103ce816103c684611ac6565b8a9190611b35565b6103da825f6001611b5a565b50505b855186906103ea906131c2565b63ffffffff1690526103fb86611c31565b5f85815260018701602052604090819020919091555133907fccf4370403e5fbbde0cd3f13426479dcd8a5916b05db424b7a2c04978cf8ce6e90610446908a908d908d908c906131ff565b60405180910390a2505050505050505050565b85515f819003610495576040517fdf83e67900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8084146104ce576040517f9ad467b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6104d7611372565b90505f6104e26113a5565b90506104ed886113d2565b5f5b838110156105235761051b8a828151811061050c5761050c613277565b60200260200101518a85611434565b6001016104ef565b505f610530858a85611520565b90508585608001818151610544919061305e565b905250610555858a83878787611645565b85156105645761056486611732565b5f5b84811015610630575f8b828151811061058157610581613277565b602002602001015190505f8a8a8481811061059e5761059e613277565b90506020028101906105b091906132a4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505060405192935033927f48a3ea0796746043948f6341d17ff8200937b99262a0b48c2663b951ed7114e5925061061e91508f90869086908e90613352565b60405180910390a25050600101610566565b5050505050505050505050565b5f839003610677576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6106b38383808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506118d592505050565b90505f5b84811015610814576107506106ca611372565b5f8888858181106106dd576106dd613277565b90506020028101906106ef91906132a4565b336040516020016107029392919061316e565b6040516020818303038152906040528051906020012081526020019081526020015f2054837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe919091161490565b6107ac5785858281811061076657610766613277565b905060200281019061077891906132a4565b6040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b337fb4b20ffb2eb1f020be3df600b2287914f50c07003526d3a9d89a9dd12351828c85858989868181106107e2576107e2613277565b90506020028101906107f491906132a4565b604051610804949392919061341b565b60405180910390a26001016106b7565b505050505050565b6108c6610827611372565b6040515f9061083e9088908890339060200161316e565b6040516020818303038152906040528051906020012081526020019081526020015f205461089d8484808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506118d592505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe919091161490565b6109005783836040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b3373ffffffffffffffffffffffffffffffffffffffff167fb4b20ffb2eb1f020be3df600b2287914f50c07003526d3a9d89a9dd12351828c8383878760405161094c949392919061341b565b60405180910390a250505050565b825f819003610995576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61099e611372565b90505f6109ad84338785611812565b90505f6109b9866118d5565b90505f805f805b87811015610a85578b8b828181106109da576109da613277565b90506020028101906109ec91906132a4565b336040516020016109ff9392919061316e565b60408051808303601f1901815291815281516020928301205f818152928a905291205490945092507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83168514610a62578b8b8281811061076657610766613277565b5f8481526020889052604081205581610a7a81613441565b9250506001016109c0565b50876060015115610acf575f610a996113a5565b90505f610aa98b5f858b8661192b565b509050610ac181610ab984611ac6565b8c9190611b35565b610acc825f85611b5a565b50505b80885f01818151610ae09190613463565b63ffffffff16905250610af288611c31565b5f8681526001880160205260408120919091555b87811015610b7657337fccf4370403e5fbbde0cd3f13426479dcd8a5916b05db424b7a2c04978cf8ce6e8b8e8e85818110610b4357610b43613277565b9050602002810190610b5591906132a4565b8d604051610b6694939291906131ff565b60405180910390a2600101610b06565b505050505050505050505050565b5f610b8d611372565b90505f610bd1338787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b9050826060015115610c0f576040517f3babafd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610c186113a5565b90505f80610c5e8989808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250508951600192509050888761192b565b915091508686608001818151610c74919061305e565b9052506001606087015267ffffffffffffffff82166040870152610c9783611ac6565b67ffffffffffffffff1660208701528551610cb6908490600190611b5a565b82546001840154610cfe918891849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b15610d35576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d3e86611c31565b5f8581526001870160205260409020558615610d5d57610d5d87611732565b3373ffffffffffffffffffffffffffffffffffffffff167fc803f8c01343fcdaf32068f4c283951623ef2b3fa0c547551931356f456b68598a8a8960405161044693929190613487565b5f610db0611372565b90505f610df4338787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b9050610dff83611d71565b5f610e086113a5565b90505f846060015115610f10575f87815b81811015610ef7575f610e2a611372565b6006015f8d8d85818110610e4057610e40613277565b9050602002016020810190610e5591906134ec565b67ffffffffffffffff908116825260208201929092526040015f2080546002820154919350640100000000900490911690610e969063ffffffff1643613505565b610ea09190613526565b6002820154610ec19190640100000000900467ffffffffffffffff16613552565b610ecb9085613552565b8154909450610eec90640100000000900467ffffffffffffffff1686613552565b945050600101610e19565b5050610f0e81610f0685611ac6565b889190611b35565b505b8585608001511015610f4e576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8585608001818151610f609190613573565b90525060608501518015610f7a5750845163ffffffff1615155b8015610fc9575081546001830154610fc9918791849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b15611000576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61100985611c31565b5f8481526001860160205260409020556110233387611dac565b3373ffffffffffffffffffffffffffffffffffffffff167f39d1320bbda24947e77f3560661323384aa0a1cb9d5e040e617e5cbf50b6dbe08989898960405161106f9493929190613586565b60405180910390a25050505050505050565b5f61108a611372565b90505f6110ce878787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b905083836080018181516110e2919061305e565b9052506110ee83611c31565b5f82815260018401602052604090205561110784611732565b8673ffffffffffffffffffffffffffffffffffffffff167f2bac1912f2481d12f0df08647c06bee174967c62d3a03cbc078eb215dc1bd9a2878787876040516111539493929190613586565b60405180910390a250505050505050565b5f61116d611372565b90505f6111b1868686808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b90506111bc83611d71565b5f6111c56113a5565b90505f8061120a8888808060200260200160405190810160405280939291908181526020018383602002808284375f9201829052508b5190935091508990508761192b565b915091506112238261121b85611ac6565b889190611e85565b5f73ffffffffffffffffffffffffffffffffffffffff8a16331480159061128f57508354600185015461128d918991859167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b155b156112c6576040517f60300a8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86516112d59085905f90611b5a565b6080870151156112eb57506080860180515f9091525b5f6040880181905260208801819052606088015261130887611c31565b5f8681526001880160205260409020558015611328576113283382611dac565b8973ffffffffffffffffffffffffffffffffffffffff167f1fce24c373e07f89214e9187598635036111dbb363e99f4ce498488cdc66e6888a8a8a60405161026f93929190613487565b5f8061139f60017fd56c4f4aab8ca22f9fde432777379f436593c6027698a6995e2daea890bed105613573565b92915050565b5f8061139f60017f0f1d85405047bdb6b0a60e27531f52a1f7a948613527b9b83a7552558207aa16613573565b805160048110806113e35750600d81115b806113f957506113f46003826135f1565b600114155b15611430576040517f3818622400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b825160301461146f576040517f637297a400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8333604051602001611483929190613629565b60408051601f1981840301815291815281516020928301205f81815292859052912054909150156114e257836040517f388e799900000000000000000000000000000000000000000000000000000000815260040161037b9190613672565b6001836040516020016114f591906136b2565b60408051601f1981840301815291815281516020928301205f94855294909152909120911790555050565b5f33836040516020016115349291906136bd565b60408051601f1981840301815291815281516020928301205f81815260018601909352912054909150806115f357845163ffffffff161515806115845750602085015167ffffffffffffffff1615155b8061159c5750604085015167ffffffffffffffff1615155b806115aa5750608085015115155b806115b757508460600151155b156115ee576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d565b6115fc85611c31565b8114611634576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d85611d71565b509392505050565b5f8061165387868686611f32565b9150915061166a888361166586611ac6565b611b35565b61167683600187611b5a565b84885f0181815161168791906136f3565b63ffffffff16905250825460018401546116d8918a91849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b1561170f576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61171888611c31565b5f9687526001909401602052505060409093205550505050565b61173a611372565b600701546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810183905273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd906064016020604051808303815f875af11580156117b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117d99190613710565b61180f576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f83836040516020016118269291906136bd565b6040516020818303038152906040528051906020012090505f61184886611c31565b5f83815260018501602052604090205490915080611892576040517f185e2b1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8181146118cb576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b6040515f907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90819061190c9085906020016136b2565b6040516020818303038152906040528051906020012016915050919050565b84515f908190815b81811015611aba575f89828151811061194e5761194e613277565b60209081029190910181015167ffffffffffffffff81165f90815260068a01909252604090912060028101549192509063ffffffff1615611a8a5761199281612527565b896119d3578054899082905f906119b090849063ffffffff16613463565b92506101000a81548163ffffffff021916908363ffffffff160217905550611a69565b8654815463ffffffff6c010000000000000000000000009092048216918b9184915f91611a02918591166136f3565b92506101000a81548163ffffffff021916908363ffffffff160217905563ffffffff161115611a69576040517f639f585100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8316600482015260240161037b565b8054611a8790640100000000900467ffffffffffffffff1686613552565b94505b6002810154611aab90640100000000900467ffffffffffffffff1687613552565b95505050806001019050611933565b50509550959350505050565b80545f9067ffffffffffffffff70010000000000000000000000000000000082041690611af99063ffffffff1643613573565b611b039190613526565b825461139f91907801000000000000000000000000000000000000000000000000900467ffffffffffffffff16613552565b611b40838383611e85565b67ffffffffffffffff918216604084015216602090910152565b611b638361264b565b81611bad57825481908490600490611b8a908490640100000000900463ffffffff16613463565b92506101000a81548163ffffffff021916908363ffffffff160217905550505050565b825463ffffffff9082908590600490611bd1908490640100000000900486166136f3565b92506101000a81548163ffffffff021916908363ffffffff160217905563ffffffff161115611c2c576040517f91aa301700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b80516020808301516040808501516080860151606087015192515f96611ccf96909594910160e09590951b7fffffffff0000000000000000000000000000000000000000000000000000000016855260c093841b7fffffffffffffffff00000000000000000000000000000000000000000000000090811660048701529290931b909116600c8401526014830152151560f81b603482015260350190565b604051602081830303815290604052805190602001209050919050565b84515f9063ffffffff1615611d6857611d0e8267ffffffffffffffff166126ca565b86608001511015611d2157506001611d68565b85515f9063ffffffff16611d358688613552565b611d3f9086613526565b611d499190613526565b9050611d5e8167ffffffffffffffff166126ca565b8760800151109150505b95945050505050565b806060015161180f576040517f95a0cf3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611db4611372565b600701546040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490529091169063a9059cbb906044016020604051808303815f875af1158015611e2b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e4f9190613710565b611430576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401515f9163ffffffff1690611e9f9084613505565b611ea99190613526565b90505f81855f015163ffffffff16866040015186611ec79190613505565b611ed19190613526565b611edb9190613552565b90508460800151611ef58267ffffffffffffffff166126ca565b11611f2157611f0d8167ffffffffffffffff166126ca565b8560800151611f1c9190613573565b611f23565b5f5b60809095019490945250505050565b83515f908190817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81805b84811015612519575f8b8281518110611f7857611f78613277565b6020026020010151905085826001611f90919061305e565b1015612077578b611fa283600161305e565b81518110611fb257611fb2613277565b602002602001015167ffffffffffffffff168167ffffffffffffffff161115612007576040517fdd020e2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8b61201383600161305e565b8151811061202357612023613277565b602002602001015167ffffffffffffffff168167ffffffffffffffff1603612077576040517fa5a1ff5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8082165f90815260068c0160209081526040808320815160a081018352815463ffffffff808216835264010000000080830489168488015273ffffffffffffffffffffffffffffffffffffffff6c01000000000000000000000000938490041684870152600185015460ff1615156060808601919091528651908101875260029095015480831686529081048916968501969096529404909516918101919091526080840181905251169003612162576040517f961e3e8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060600151156123385766ffffffffffffff600883901c1695508486146121a557335f90815260098c016020908152604080832089845290915290205486955093505b600160ff83161b84165f036123385767ffffffffffffffff82165f90815260048c01602052604090205473ffffffffffffffffffffffffffffffffffffffff1680612228576040517fb7f529fe00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8416600482015260240161037b565b73ffffffffffffffffffffffffffffffffffffffff811633146123365761224e816126e2565b15806122f357506040517f830639ac00000000000000000000000000000000000000000000000000000000815233600482015267ffffffffffffffff8416602482015273ffffffffffffffffffffffffffffffffffffffff82169063830639ac90604401602060405180830381865afa1580156122cd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f19190613710565b155b15612336576040517fb7f529fe00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8416600482015260240161037b565b505b6123418161270d565b895481516c0100000000000000000000000090910463ffffffff16908d90839061236c9083906136f3565b63ffffffff1690819052919091111590506123bf576040517f639f585100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8316600482015260240161037b565b60208101516123ce9089613552565b9750806080015160200151896123e49190613552565b67ffffffffffffffff9283165f90815260068d01602090815260409182902084518154868401518786015163ffffffff9384167fffffffffffffffffffffffffffffffffffffffff00000000000000000000000093841617640100000000928b168302176bffffffffffffffffffffffff166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90921682021785556060890151600180870180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001692151592909217909155608090990151805160029096018054978201519190980151959094169590921694909417918816909302177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff169516029390931790925590975001611f5d565b505050505094509492505050565b805460028201545f91640100000000900467ffffffffffffffff16906125539063ffffffff1643613463565b63ffffffff166125639190613526565b600283018054919250829160049061258e908490640100000000900467ffffffffffffffff16613552565b825467ffffffffffffffff9182166101009390930a92830291909202199091161790555081546125c49063ffffffff1682613526565b600283018054600c906125f29084906c01000000000000000000000000900467ffffffffffffffff16613552565b82546101009290920a67ffffffffffffffff818102199093169190921691909102179055505060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000164363ffffffff16179055565b6126548161279e565b6001820180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff9290921691909117905580547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff16680100000000000000004363ffffffff1602179055565b5f61139f6298968067ffffffffffffffff841661372b565b5f61139f827f830639ac00000000000000000000000000000000000000000000000000000000612815565b60208101516080820151515f91906127259043613463565b63ffffffff166127359190613526565b905080826080015160200181815161274d9190613552565b67ffffffffffffffff16905250815161276c9063ffffffff1682613526565b82608001516040018181516127819190613552565b67ffffffffffffffff16905250506080015163ffffffff43169052565b80545f9063ffffffff640100000000820481169167ffffffffffffffff700100000000000000000000000000000000820416916127e8916801000000000000000090041643613505565b6127f29190613526565b6127fc9190613526565b600183015461139f919067ffffffffffffffff16613552565b5f61281f83612837565b80156128305750612830838361289a565b9392505050565b5f612862827f01ffc9a70000000000000000000000000000000000000000000000000000000061289a565b801561139f5750612893827fffffffff0000000000000000000000000000000000000000000000000000000061289a565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a70000000000000000000000000000000000000000000000000000000017815282515f9392849283928392918391908a617530fa92503d91505f519050828015612950575060208210155b801561295b57505f81115b979650505050505050565b5f8083601f840112612976575f80fd5b50813567ffffffffffffffff81111561298d575f80fd5b6020830191508360208285010111156129a4575f80fd5b9250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612a0157612a016129ab565b604052919050565b5f67ffffffffffffffff821115612a2257612a226129ab565b5060051b60200190565b803567ffffffffffffffff81168114612a43575f80fd5b919050565b5f82601f830112612a57575f80fd5b81356020612a6c612a6783612a09565b6129d8565b8083825260208201915060208460051b870101935086841115612a8d575f80fd5b602086015b84811015612ab057612aa381612a2c565b8352918301918301612a92565b509695505050505050565b801515811461180f575f80fd5b5f60a08284031215612ad8575f80fd5b60405160a0810181811067ffffffffffffffff82111715612afb57612afb6129ab565b604052905080823563ffffffff81168114612b14575f80fd5b8152612b2260208401612a2c565b6020820152612b3360408401612a2c565b60408201526060830135612b4681612abb565b6060820152608092830135920191909152919050565b5f805f805f805f610120888a031215612b73575f80fd5b873567ffffffffffffffff80821115612b8a575f80fd5b612b968b838c01612966565b909950975060208a0135915080821115612bae575f80fd5b612bba8b838c01612a48565b965060408a0135915080821115612bcf575f80fd5b50612bdc8a828b01612966565b90955093505060608801359150612bf68960808a01612ac8565b905092959891949750929550565b5f805f8060e08587031215612c17575f80fd5b843567ffffffffffffffff80821115612c2e575f80fd5b612c3a88838901612966565b90965094506020870135915080821115612c52575f80fd5b50612c5f87828801612a48565b925050612c6f8660408701612ac8565b905092959194509250565b5f8083601f840112612c8a575f80fd5b50813567ffffffffffffffff811115612ca1575f80fd5b6020830191508360208260051b85010111156129a4575f80fd5b5f805f805f806101208789031215612cd1575f80fd5b67ffffffffffffffff8088351115612ce7575f80fd5b8735880189601f820112612cf9575f80fd5b612d06612a678235612a09565b81358082526020808301929160051b8401018c1015612d23575f80fd5b602083015b6020843560051b850101811015612dbd578481351115612d46575f80fd5b803584018d603f820112612d58575f80fd5b602081013586811115612d6d57612d6d6129ab565b612d806020601f19601f840116016129d8565b8181528f6040838501011115612d94575f80fd5b816040840160208301375f60208383010152808652505050602083019250602081019050612d28565b50985050506020880135811015612dd2575f80fd5b612de28960208a01358a01612a48565b95508060408901351115612df4575f80fd5b50612e058860408901358901612c7a565b909450925060608701359150612e1e8860808901612ac8565b90509295509295509295565b5f805f8060408587031215612e3d575f80fd5b843567ffffffffffffffff80821115612e54575f80fd5b612e6088838901612c7a565b90965094506020870135915080821115612e78575f80fd5b50612e8587828801612c7a565b95989497509550505050565b5f805f8060408587031215612ea4575f80fd5b843567ffffffffffffffff80821115612ebb575f80fd5b612e6088838901612966565b5f805f8060e08587031215612eda575f80fd5b843567ffffffffffffffff80821115612ef1575f80fd5b612c3a88838901612c7a565b5f805f8060e08587031215612f10575f80fd5b843567ffffffffffffffff811115612f26575f80fd5b612f3287828801612c7a565b90955093505060208501359150612c6f8660408701612ac8565b803573ffffffffffffffffffffffffffffffffffffffff81168114612a43575f80fd5b5f805f805f6101008688031215612f84575f80fd5b612f8d86612f4c565b9450602086013567ffffffffffffffff811115612fa8575f80fd5b612fb488828901612c7a565b90955093505060408601359150612fce8760608801612ac8565b90509295509295909350565b5f805f8060e08587031215612fed575f80fd5b612ff685612f4c565b9350602085013567ffffffffffffffff811115613011575f80fd5b61301d87828801612c7a565b9094509250612c6f90508660408701612ac8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082018082111561139f5761139f613031565b5f815180845260208085019450602084015f5b838110156130aa57815167ffffffffffffffff1687529582019590820190600101613084565b509495945050505050565b81835281816020850137505f602082840101525f6020601f19601f840116840101905092915050565b5f6101008083526130f18184018a613071565b9050828103602084015261310681888a6130b5565b9050828103604084015261311b8186886130b5565b91505061295b606083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b8284823760609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169101908152601401919050565b602081525f6131ba6020830184866130b5565b949350505050565b5f63ffffffff8216806131d7576131d7613031565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60e081525f61321160e0830187613071565b82810360208401526132248186886130b5565b915050611d68604083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126132d7575f80fd5b83018035915067ffffffffffffffff8211156132f1575f80fd5b6020019150368190038213156129a4575f80fd5b5f5b8381101561331f578181015183820152602001613307565b50505f910152565b5f815180845261333e816020860160208601613305565b601f01601f19169290920160200192915050565b5f61010080835261336581840188613071565b905082810360208401526133798187613327565b9050828103604084015261338d8186613327565b915050611d68606083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b8183525f60208085019450825f5b858110156130aa5767ffffffffffffffff61340883612a2c565b16875295820195908201906001016133ee565b604081525f61342e6040830186886133e0565b828103602084015261295b8185876130b5565b5f63ffffffff80831681810361345957613459613031565b6001019392505050565b63ffffffff82811682821603908082111561348057613480613031565b5092915050565b60c081525f61349a60c0830185876133e0565b90506131ba602083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b5f602082840312156134fc575f80fd5b61283082612a2c565b67ffffffffffffffff82811682821603908082111561348057613480613031565b67ffffffffffffffff81811683821602808216919082811461354a5761354a613031565b505092915050565b67ffffffffffffffff81811683821601908082111561348057613480613031565b8181038181111561139f5761139f613031565b60e081525f61359960e0830186886133e0565b9050836020830152611d68604083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b5f82613624577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500690565b5f835161363a818460208801613305565b60609390931b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169190920190815260140192915050565b602081525f6128306020830184613327565b80515f9060208084018383156130aa57815167ffffffffffffffff1687529582019590820190600101613084565b5f6128308284613684565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008360601b1681525f6131ba6014830184613684565b63ffffffff81811683821601908082111561348057613480613031565b5f60208284031215613720575f80fd5b815161283081612abb565b808202811582820484141761139f5761139f61303156fea26469706673582212201e854e2755e24f7c4b395af307c15de1c046c65db1bd09ae205a35d9392aec7e64736f6c63430008180033
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.