Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xAF83e07C...2D352acA3 The constructor portion of the code might be different and could alter the actual behaviour of the contract
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.24; 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.24; 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.24; 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"}]
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106100b9575f3560e01c80635aed114211610072578063686e682c11610058578063686e682c14610144578063bc26e7e514610157578063bf0f2fb21461016a575f80fd5b80635aed11421461011e5780635fec6dd014610131575f80fd5b806322f18bf5116100a257806322f18bf5146100e557806332afd02f146100f85780633877322b1461010b575f80fd5b806306e8fb9c146100bd57806312b3fc19146100d2575b5f80fd5b6100d06100cb366004612b5c565b61017d565b005b6100d06100e0366004612c04565b610283565b6100d06100f3366004612cbb565b610459565b6100d0610106366004612e2a565b61063d565b6100d0610119366004612e91565b61081c565b6100d061012c366004612ec7565b61095a565b6100d061013f366004612efd565b610b84565b6100d0610152366004612efd565b610da7565b6100d0610165366004612f6f565b611081565b6100d0610178366004612fda565b611164565b5f610186611372565b90505f6101916113a5565b905061019c876113d2565b6101de89898080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508b92508691506114349050565b5f6101ea848985611520565b905084846080018181516101fe919061305e565b90525061021084898360018787611645565b841561021f5761021f85611732565b3373ffffffffffffffffffffffffffffffffffffffff167f48a3ea0796746043948f6341d17ff8200937b99262a0b48c2663b951ed7114e5898c8c8b8b8a60405161026f969594939291906130de565b60405180910390a250505050505050505050565b5f61028c611372565b90505f61029b83338685611812565b90505f6102a7856118d5565b90505f8787336040516020016102bf9392919061316e565b60408051601f1981840301815291815281516020928301205f818152928790529120549091508061031c576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe811683146103845788886040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b60405180910390fd5b5f828152602086905260408120556060860151156103dd575f6103a56113a5565b90505f6103b6895f60018a8661192b565b5090506103ce816103c684611ac6565b8a9190611b35565b6103da825f6001611b5a565b50505b855186906103ea906131c2565b63ffffffff1690526103fb86611c31565b5f85815260018701602052604090819020919091555133907fccf4370403e5fbbde0cd3f13426479dcd8a5916b05db424b7a2c04978cf8ce6e90610446908a908d908d908c906131ff565b60405180910390a2505050505050505050565b85515f819003610495576040517fdf83e67900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8084146104ce576040517f9ad467b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6104d7611372565b90505f6104e26113a5565b90506104ed886113d2565b5f5b838110156105235761051b8a828151811061050c5761050c613277565b60200260200101518a85611434565b6001016104ef565b505f610530858a85611520565b90508585608001818151610544919061305e565b905250610555858a83878787611645565b85156105645761056486611732565b5f5b84811015610630575f8b828151811061058157610581613277565b602002602001015190505f8a8a8481811061059e5761059e613277565b90506020028101906105b091906132a4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505060405192935033927f48a3ea0796746043948f6341d17ff8200937b99262a0b48c2663b951ed7114e5925061061e91508f90869086908e90613352565b60405180910390a25050600101610566565b5050505050505050505050565b5f839003610677576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6106b38383808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506118d592505050565b90505f5b84811015610814576107506106ca611372565b5f8888858181106106dd576106dd613277565b90506020028101906106ef91906132a4565b336040516020016107029392919061316e565b6040516020818303038152906040528051906020012081526020019081526020015f2054837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe919091161490565b6107ac5785858281811061076657610766613277565b905060200281019061077891906132a4565b6040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b337fb4b20ffb2eb1f020be3df600b2287914f50c07003526d3a9d89a9dd12351828c85858989868181106107e2576107e2613277565b90506020028101906107f491906132a4565b604051610804949392919061341b565b60405180910390a26001016106b7565b505050505050565b6108c6610827611372565b6040515f9061083e9088908890339060200161316e565b6040516020818303038152906040528051906020012081526020019081526020015f205461089d8484808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506118d592505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe919091161490565b6109005783836040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b3373ffffffffffffffffffffffffffffffffffffffff167fb4b20ffb2eb1f020be3df600b2287914f50c07003526d3a9d89a9dd12351828c8383878760405161094c949392919061341b565b60405180910390a250505050565b825f819003610995576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61099e611372565b90505f6109ad84338785611812565b90505f6109b9866118d5565b90505f805f805b87811015610a85578b8b828181106109da576109da613277565b90506020028101906109ec91906132a4565b336040516020016109ff9392919061316e565b60408051808303601f1901815291815281516020928301205f818152928a905291205490945092507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83168514610a62578b8b8281811061076657610766613277565b5f8481526020889052604081205581610a7a81613441565b9250506001016109c0565b50876060015115610acf575f610a996113a5565b90505f610aa98b5f858b8661192b565b509050610ac181610ab984611ac6565b8c9190611b35565b610acc825f85611b5a565b50505b80885f01818151610ae09190613463565b63ffffffff16905250610af288611c31565b5f8681526001880160205260408120919091555b87811015610b7657337fccf4370403e5fbbde0cd3f13426479dcd8a5916b05db424b7a2c04978cf8ce6e8b8e8e85818110610b4357610b43613277565b9050602002810190610b5591906132a4565b8d604051610b6694939291906131ff565b60405180910390a2600101610b06565b505050505050505050505050565b5f610b8d611372565b90505f610bd1338787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b9050826060015115610c0f576040517f3babafd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610c186113a5565b90505f80610c5e8989808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250508951600192509050888761192b565b915091508686608001818151610c74919061305e565b9052506001606087015267ffffffffffffffff82166040870152610c9783611ac6565b67ffffffffffffffff1660208701528551610cb6908490600190611b5a565b82546001840154610cfe918891849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b15610d35576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d3e86611c31565b5f8581526001870160205260409020558615610d5d57610d5d87611732565b3373ffffffffffffffffffffffffffffffffffffffff167fc803f8c01343fcdaf32068f4c283951623ef2b3fa0c547551931356f456b68598a8a8960405161044693929190613487565b5f610db0611372565b90505f610df4338787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b9050610dff83611d71565b5f610e086113a5565b90505f846060015115610f10575f87815b81811015610ef7575f610e2a611372565b6006015f8d8d85818110610e4057610e40613277565b9050602002016020810190610e5591906134ec565b67ffffffffffffffff908116825260208201929092526040015f2080546002820154919350640100000000900490911690610e969063ffffffff1643613505565b610ea09190613526565b6002820154610ec19190640100000000900467ffffffffffffffff16613552565b610ecb9085613552565b8154909450610eec90640100000000900467ffffffffffffffff1686613552565b945050600101610e19565b5050610f0e81610f0685611ac6565b889190611b35565b505b8585608001511015610f4e576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8585608001818151610f609190613573565b90525060608501518015610f7a5750845163ffffffff1615155b8015610fc9575081546001830154610fc9918791849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b15611000576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61100985611c31565b5f8481526001860160205260409020556110233387611dac565b3373ffffffffffffffffffffffffffffffffffffffff167f39d1320bbda24947e77f3560661323384aa0a1cb9d5e040e617e5cbf50b6dbe08989898960405161106f9493929190613586565b60405180910390a25050505050505050565b5f61108a611372565b90505f6110ce878787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b905083836080018181516110e2919061305e565b9052506110ee83611c31565b5f82815260018401602052604090205561110784611732565b8673ffffffffffffffffffffffffffffffffffffffff167f2bac1912f2481d12f0df08647c06bee174967c62d3a03cbc078eb215dc1bd9a2878787876040516111539493929190613586565b60405180910390a250505050505050565b5f61116d611372565b90505f6111b1868686808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b90506111bc83611d71565b5f6111c56113a5565b90505f8061120a8888808060200260200160405190810160405280939291908181526020018383602002808284375f9201829052508b5190935091508990508761192b565b915091506112238261121b85611ac6565b889190611e85565b5f73ffffffffffffffffffffffffffffffffffffffff8a16331480159061128f57508354600185015461128d918991859167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b155b156112c6576040517f60300a8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86516112d59085905f90611b5a565b6080870151156112eb57506080860180515f9091525b5f6040880181905260208801819052606088015261130887611c31565b5f8681526001880160205260409020558015611328576113283382611dac565b8973ffffffffffffffffffffffffffffffffffffffff167f1fce24c373e07f89214e9187598635036111dbb363e99f4ce498488cdc66e6888a8a8a60405161026f93929190613487565b5f8061139f60017fd56c4f4aab8ca22f9fde432777379f436593c6027698a6995e2daea890bed105613573565b92915050565b5f8061139f60017f0f1d85405047bdb6b0a60e27531f52a1f7a948613527b9b83a7552558207aa16613573565b805160048110806113e35750600d81115b806113f957506113f46003826135f1565b600114155b15611430576040517f3818622400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b825160301461146f576040517f637297a400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8333604051602001611483929190613629565b60408051601f1981840301815291815281516020928301205f81815292859052912054909150156114e257836040517f388e799900000000000000000000000000000000000000000000000000000000815260040161037b9190613672565b6001836040516020016114f591906136b2565b60408051601f1981840301815291815281516020928301205f94855294909152909120911790555050565b5f33836040516020016115349291906136bd565b60408051601f1981840301815291815281516020928301205f81815260018601909352912054909150806115f357845163ffffffff161515806115845750602085015167ffffffffffffffff1615155b8061159c5750604085015167ffffffffffffffff1615155b806115aa5750608085015115155b806115b757508460600151155b156115ee576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d565b6115fc85611c31565b8114611634576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d85611d71565b509392505050565b5f8061165387868686611f32565b9150915061166a888361166586611ac6565b611b35565b61167683600187611b5a565b84885f0181815161168791906136f3565b63ffffffff16905250825460018401546116d8918a91849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b1561170f576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61171888611c31565b5f9687526001909401602052505060409093205550505050565b61173a611372565b600701546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810183905273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd906064016020604051808303815f875af11580156117b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117d99190613710565b61180f576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f83836040516020016118269291906136bd565b6040516020818303038152906040528051906020012090505f61184886611c31565b5f83815260018501602052604090205490915080611892576040517f185e2b1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8181146118cb576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b6040515f907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90819061190c9085906020016136b2565b6040516020818303038152906040528051906020012016915050919050565b84515f908190815b81811015611aba575f89828151811061194e5761194e613277565b60209081029190910181015167ffffffffffffffff81165f90815260068a01909252604090912060028101549192509063ffffffff1615611a8a5761199281612527565b896119d3578054899082905f906119b090849063ffffffff16613463565b92506101000a81548163ffffffff021916908363ffffffff160217905550611a69565b8654815463ffffffff6c010000000000000000000000009092048216918b9184915f91611a02918591166136f3565b92506101000a81548163ffffffff021916908363ffffffff160217905563ffffffff161115611a69576040517f639f585100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8316600482015260240161037b565b8054611a8790640100000000900467ffffffffffffffff1686613552565b94505b6002810154611aab90640100000000900467ffffffffffffffff1687613552565b95505050806001019050611933565b50509550959350505050565b80545f9067ffffffffffffffff70010000000000000000000000000000000082041690611af99063ffffffff1643613573565b611b039190613526565b825461139f91907801000000000000000000000000000000000000000000000000900467ffffffffffffffff16613552565b611b40838383611e85565b67ffffffffffffffff918216604084015216602090910152565b611b638361264b565b81611bad57825481908490600490611b8a908490640100000000900463ffffffff16613463565b92506101000a81548163ffffffff021916908363ffffffff160217905550505050565b825463ffffffff9082908590600490611bd1908490640100000000900486166136f3565b92506101000a81548163ffffffff021916908363ffffffff160217905563ffffffff161115611c2c576040517f91aa301700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b80516020808301516040808501516080860151606087015192515f96611ccf96909594910160e09590951b7fffffffff0000000000000000000000000000000000000000000000000000000016855260c093841b7fffffffffffffffff00000000000000000000000000000000000000000000000090811660048701529290931b909116600c8401526014830152151560f81b603482015260350190565b604051602081830303815290604052805190602001209050919050565b84515f9063ffffffff1615611d6857611d0e8267ffffffffffffffff166126ca565b86608001511015611d2157506001611d68565b85515f9063ffffffff16611d358688613552565b611d3f9086613526565b611d499190613526565b9050611d5e8167ffffffffffffffff166126ca565b8760800151109150505b95945050505050565b806060015161180f576040517f95a0cf3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611db4611372565b600701546040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490529091169063a9059cbb906044016020604051808303815f875af1158015611e2b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e4f9190613710565b611430576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401515f9163ffffffff1690611e9f9084613505565b611ea99190613526565b90505f81855f015163ffffffff16866040015186611ec79190613505565b611ed19190613526565b611edb9190613552565b90508460800151611ef58267ffffffffffffffff166126ca565b11611f2157611f0d8167ffffffffffffffff166126ca565b8560800151611f1c9190613573565b611f23565b5f5b60809095019490945250505050565b83515f908190817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81805b84811015612519575f8b8281518110611f7857611f78613277565b6020026020010151905085826001611f90919061305e565b1015612077578b611fa283600161305e565b81518110611fb257611fb2613277565b602002602001015167ffffffffffffffff168167ffffffffffffffff161115612007576040517fdd020e2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8b61201383600161305e565b8151811061202357612023613277565b602002602001015167ffffffffffffffff168167ffffffffffffffff1603612077576040517fa5a1ff5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8082165f90815260068c0160209081526040808320815160a081018352815463ffffffff808216835264010000000080830489168488015273ffffffffffffffffffffffffffffffffffffffff6c01000000000000000000000000938490041684870152600185015460ff1615156060808601919091528651908101875260029095015480831686529081048916968501969096529404909516918101919091526080840181905251169003612162576040517f961e3e8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060600151156123385766ffffffffffffff600883901c1695508486146121a557335f90815260098c016020908152604080832089845290915290205486955093505b600160ff83161b84165f036123385767ffffffffffffffff82165f90815260048c01602052604090205473ffffffffffffffffffffffffffffffffffffffff1680612228576040517fb7f529fe00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8416600482015260240161037b565b73ffffffffffffffffffffffffffffffffffffffff811633146123365761224e816126e2565b15806122f357506040517f830639ac00000000000000000000000000000000000000000000000000000000815233600482015267ffffffffffffffff8416602482015273ffffffffffffffffffffffffffffffffffffffff82169063830639ac90604401602060405180830381865afa1580156122cd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f19190613710565b155b15612336576040517fb7f529fe00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8416600482015260240161037b565b505b6123418161270d565b895481516c0100000000000000000000000090910463ffffffff16908d90839061236c9083906136f3565b63ffffffff1690819052919091111590506123bf576040517f639f585100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8316600482015260240161037b565b60208101516123ce9089613552565b9750806080015160200151896123e49190613552565b67ffffffffffffffff9283165f90815260068d01602090815260409182902084518154868401518786015163ffffffff9384167fffffffffffffffffffffffffffffffffffffffff00000000000000000000000093841617640100000000928b168302176bffffffffffffffffffffffff166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90921682021785556060890151600180870180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001692151592909217909155608090990151805160029096018054978201519190980151959094169590921694909417918816909302177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff169516029390931790925590975001611f5d565b505050505094509492505050565b805460028201545f91640100000000900467ffffffffffffffff16906125539063ffffffff1643613463565b63ffffffff166125639190613526565b600283018054919250829160049061258e908490640100000000900467ffffffffffffffff16613552565b825467ffffffffffffffff9182166101009390930a92830291909202199091161790555081546125c49063ffffffff1682613526565b600283018054600c906125f29084906c01000000000000000000000000900467ffffffffffffffff16613552565b82546101009290920a67ffffffffffffffff818102199093169190921691909102179055505060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000164363ffffffff16179055565b6126548161279e565b6001820180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff9290921691909117905580547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff16680100000000000000004363ffffffff1602179055565b5f61139f6298968067ffffffffffffffff841661372b565b5f61139f827f830639ac00000000000000000000000000000000000000000000000000000000612815565b60208101516080820151515f91906127259043613463565b63ffffffff166127359190613526565b905080826080015160200181815161274d9190613552565b67ffffffffffffffff16905250815161276c9063ffffffff1682613526565b82608001516040018181516127819190613552565b67ffffffffffffffff16905250506080015163ffffffff43169052565b80545f9063ffffffff640100000000820481169167ffffffffffffffff700100000000000000000000000000000000820416916127e8916801000000000000000090041643613505565b6127f29190613526565b6127fc9190613526565b600183015461139f919067ffffffffffffffff16613552565b5f61281f83612837565b80156128305750612830838361289a565b9392505050565b5f612862827f01ffc9a70000000000000000000000000000000000000000000000000000000061289a565b801561139f5750612893827fffffffff0000000000000000000000000000000000000000000000000000000061289a565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a70000000000000000000000000000000000000000000000000000000017815282515f9392849283928392918391908a617530fa92503d91505f519050828015612950575060208210155b801561295b57505f81115b979650505050505050565b5f8083601f840112612976575f80fd5b50813567ffffffffffffffff81111561298d575f80fd5b6020830191508360208285010111156129a4575f80fd5b9250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612a0157612a016129ab565b604052919050565b5f67ffffffffffffffff821115612a2257612a226129ab565b5060051b60200190565b803567ffffffffffffffff81168114612a43575f80fd5b919050565b5f82601f830112612a57575f80fd5b81356020612a6c612a6783612a09565b6129d8565b8083825260208201915060208460051b870101935086841115612a8d575f80fd5b602086015b84811015612ab057612aa381612a2c565b8352918301918301612a92565b509695505050505050565b801515811461180f575f80fd5b5f60a08284031215612ad8575f80fd5b60405160a0810181811067ffffffffffffffff82111715612afb57612afb6129ab565b604052905080823563ffffffff81168114612b14575f80fd5b8152612b2260208401612a2c565b6020820152612b3360408401612a2c565b60408201526060830135612b4681612abb565b6060820152608092830135920191909152919050565b5f805f805f805f610120888a031215612b73575f80fd5b873567ffffffffffffffff80821115612b8a575f80fd5b612b968b838c01612966565b909950975060208a0135915080821115612bae575f80fd5b612bba8b838c01612a48565b965060408a0135915080821115612bcf575f80fd5b50612bdc8a828b01612966565b90955093505060608801359150612bf68960808a01612ac8565b905092959891949750929550565b5f805f8060e08587031215612c17575f80fd5b843567ffffffffffffffff80821115612c2e575f80fd5b612c3a88838901612966565b90965094506020870135915080821115612c52575f80fd5b50612c5f87828801612a48565b925050612c6f8660408701612ac8565b905092959194509250565b5f8083601f840112612c8a575f80fd5b50813567ffffffffffffffff811115612ca1575f80fd5b6020830191508360208260051b85010111156129a4575f80fd5b5f805f805f806101208789031215612cd1575f80fd5b67ffffffffffffffff8088351115612ce7575f80fd5b8735880189601f820112612cf9575f80fd5b612d06612a678235612a09565b81358082526020808301929160051b8401018c1015612d23575f80fd5b602083015b6020843560051b850101811015612dbd578481351115612d46575f80fd5b803584018d603f820112612d58575f80fd5b602081013586811115612d6d57612d6d6129ab565b612d806020601f19601f840116016129d8565b8181528f6040838501011115612d94575f80fd5b816040840160208301375f60208383010152808652505050602083019250602081019050612d28565b50985050506020880135811015612dd2575f80fd5b612de28960208a01358a01612a48565b95508060408901351115612df4575f80fd5b50612e058860408901358901612c7a565b909450925060608701359150612e1e8860808901612ac8565b90509295509295509295565b5f805f8060408587031215612e3d575f80fd5b843567ffffffffffffffff80821115612e54575f80fd5b612e6088838901612c7a565b90965094506020870135915080821115612e78575f80fd5b50612e8587828801612c7a565b95989497509550505050565b5f805f8060408587031215612ea4575f80fd5b843567ffffffffffffffff80821115612ebb575f80fd5b612e6088838901612966565b5f805f8060e08587031215612eda575f80fd5b843567ffffffffffffffff80821115612ef1575f80fd5b612c3a88838901612c7a565b5f805f8060e08587031215612f10575f80fd5b843567ffffffffffffffff811115612f26575f80fd5b612f3287828801612c7a565b90955093505060208501359150612c6f8660408701612ac8565b803573ffffffffffffffffffffffffffffffffffffffff81168114612a43575f80fd5b5f805f805f6101008688031215612f84575f80fd5b612f8d86612f4c565b9450602086013567ffffffffffffffff811115612fa8575f80fd5b612fb488828901612c7a565b90955093505060408601359150612fce8760608801612ac8565b90509295509295909350565b5f805f8060e08587031215612fed575f80fd5b612ff685612f4c565b9350602085013567ffffffffffffffff811115613011575f80fd5b61301d87828801612c7a565b9094509250612c6f90508660408701612ac8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082018082111561139f5761139f613031565b5f815180845260208085019450602084015f5b838110156130aa57815167ffffffffffffffff1687529582019590820190600101613084565b509495945050505050565b81835281816020850137505f602082840101525f6020601f19601f840116840101905092915050565b5f6101008083526130f18184018a613071565b9050828103602084015261310681888a6130b5565b9050828103604084015261311b8186886130b5565b91505061295b606083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b8284823760609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169101908152601401919050565b602081525f6131ba6020830184866130b5565b949350505050565b5f63ffffffff8216806131d7576131d7613031565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60e081525f61321160e0830187613071565b82810360208401526132248186886130b5565b915050611d68604083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126132d7575f80fd5b83018035915067ffffffffffffffff8211156132f1575f80fd5b6020019150368190038213156129a4575f80fd5b5f5b8381101561331f578181015183820152602001613307565b50505f910152565b5f815180845261333e816020860160208601613305565b601f01601f19169290920160200192915050565b5f61010080835261336581840188613071565b905082810360208401526133798187613327565b9050828103604084015261338d8186613327565b915050611d68606083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b8183525f60208085019450825f5b858110156130aa5767ffffffffffffffff61340883612a2c565b16875295820195908201906001016133ee565b604081525f61342e6040830186886133e0565b828103602084015261295b8185876130b5565b5f63ffffffff80831681810361345957613459613031565b6001019392505050565b63ffffffff82811682821603908082111561348057613480613031565b5092915050565b60c081525f61349a60c0830185876133e0565b90506131ba602083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b5f602082840312156134fc575f80fd5b61283082612a2c565b67ffffffffffffffff82811682821603908082111561348057613480613031565b67ffffffffffffffff81811683821602808216919082811461354a5761354a613031565b505092915050565b67ffffffffffffffff81811683821601908082111561348057613480613031565b8181038181111561139f5761139f613031565b60e081525f61359960e0830186886133e0565b9050836020830152611d68604083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b5f82613624577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500690565b5f835161363a818460208801613305565b60609390931b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169190920190815260140192915050565b602081525f6128306020830184613327565b80515f9060208084018383156130aa57815167ffffffffffffffff1687529582019590820190600101613084565b5f6128308284613684565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008360601b1681525f6131ba6014830184613684565b63ffffffff81811683821601908082111561348057613480613031565b5f60208284031215613720575f80fd5b815161283081612abb565b808202811582820484141761139f5761139f61303156fea264697066735822122001f8ca2b992b1c23e324c159bf3203b3e68ec294c5a7a5bc258834d2006e6d2764736f6c63430008180033
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.