From 98e2bd2a8827b35101e44293d463ad5be5d11829 Mon Sep 17 00:00:00 2001 From: Sanjay Yadav Date: Mon, 11 Sep 2023 18:49:36 +0530 Subject: [PATCH 1/4] feat/add SSV Pool --- contracts/DVT/SSV/SSVNodeRegistry.sol | 611 ++++++++++++++++++ contracts/DVT/SSV/SSVPool.sol | 266 ++++++++ .../DVT/SSV/SSVValidatorWithdrawalVault.sol | 226 +++++++ contracts/DVT/SSV/SSVVaultProxy.sol | 73 +++ contracts/SDCollateral.sol | 27 + contracts/StaderConfig.sol | 41 ++ contracts/StaderInsuranceFund.sol | 12 +- contracts/factory/VaultFactory.sol | 38 ++ .../interfaces/DVT/SSV/ISSVNodeRegistry.sol | 144 +++++ contracts/interfaces/DVT/SSV/ISSVPool.sol | 40 ++ .../DVT/SSV/ISSVValidatorWithdrawalVault.sol | 34 + .../interfaces/DVT/SSV/ISSVVaultProxy.sol | 29 + contracts/interfaces/IStaderConfig.sol | 16 + contracts/interfaces/IStaderInsuranceFund.sol | 1 + contracts/interfaces/IVaultFactory.sol | 11 + .../interfaces/SDCollateral/ISDCollateral.sol | 7 + .../interfaces/SSVNetwork/ISSVNetwork.sol | 189 ++++++ .../interfaces/SSVNetwork/ISSVNetworkCore.sol | 12 + .../SSVNetwork/ISSVNetworkViews.sol | 132 ++++ hardhat.config.ts | 9 + 20 files changed, 1913 insertions(+), 5 deletions(-) create mode 100644 contracts/DVT/SSV/SSVNodeRegistry.sol create mode 100644 contracts/DVT/SSV/SSVPool.sol create mode 100644 contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol create mode 100644 contracts/DVT/SSV/SSVVaultProxy.sol create mode 100644 contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol create mode 100644 contracts/interfaces/DVT/SSV/ISSVPool.sol create mode 100644 contracts/interfaces/DVT/SSV/ISSVValidatorWithdrawalVault.sol create mode 100644 contracts/interfaces/DVT/SSV/ISSVVaultProxy.sol create mode 100644 contracts/interfaces/SSVNetwork/ISSVNetwork.sol create mode 100644 contracts/interfaces/SSVNetwork/ISSVNetworkCore.sol create mode 100644 contracts/interfaces/SSVNetwork/ISSVNetworkViews.sol diff --git a/contracts/DVT/SSV/SSVNodeRegistry.sol b/contracts/DVT/SSV/SSVNodeRegistry.sol new file mode 100644 index 00000000..d13ee525 --- /dev/null +++ b/contracts/DVT/SSV/SSVNodeRegistry.sol @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import '../../library/UtilLib.sol'; +import '../../library/ValidatorStatus.sol'; + +import '../../interfaces/IPoolUtils.sol'; +import '../../interfaces/INodeRegistry.sol'; +import '../../interfaces/IStaderConfig.sol'; +import '../../interfaces/IVaultFactory.sol'; + +import '../../interfaces/DVT/SSV/ISSVPool.sol'; +import '../../interfaces/IStaderInsuranceFund.sol'; +import '../../interfaces/SSVNetwork/ISSVNetwork.sol'; +import '../../interfaces/DVT/SSV/ISSVNodeRegistry.sol'; +import '../../interfaces/IOperatorRewardsCollector.sol'; +import '../../interfaces/SDCollateral/ISDCollateral.sol'; +import '../../interfaces/SSVNetwork/ISSVNetworkViews.sol'; +import '../../interfaces/DVT/SSV/ISSVValidatorWithdrawalVault.sol'; + +import '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol'; + +contract SSVNodeRegistry is + ISSVNodeRegistry, + AccessControlUpgradeable, + PausableUpgradeable, + ReentrancyGuardUpgradeable +{ + uint8 public constant override POOL_ID = 3; + uint8 public constant override CLUSTER_SIZE = 4; + uint16 public override inputKeyCountLimit; + uint64 public batchSizeToRemoveValidatorFromSSV; + uint64 public batchSizeToRegisterValidatorFromSSV; + + IStaderConfig public staderConfig; + ISSVNetwork public ssvNetwork; + ISSVNetworkViews public ssvNetworkViews; + + uint64 public nextOperatorId; + uint256 public nextValidatorId; + uint256 public verifiedKeyBatchSize; + uint256 public nextQueuedValidatorIndex; + uint256 public totalActiveValidatorCount; + uint256 public constant COLLATERAL_ETH = 1.6 ether; + uint256 public constant COLLATERAL_ETH_PER_KEY_SHARE = 0.8 ether; + + // mapping of validator Id and Validator struct + mapping(uint256 => Validator) public validatorRegistry; + // mapping of validator public key and validator Id + mapping(bytes => uint256) public validatorIdByPubkey; + //mapping of SSV operators assigned to a validator + mapping(bytes => uint64[]) public operatorIdssByPubkey; + // mapping of operator Id and Operator struct + mapping(uint64 => SSVOperator) public operatorStructById; + // mapping of operator address and operator Id + mapping(address => uint64) public operatorIDByAddress; + // mapping of whitelisted permissioned node operator + mapping(address => bool) public permissionList; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize( + address _admin, + address _staderConfig, + address _ssvNetwork, + address _ssvNetworkViews + ) external initializer { + UtilLib.checkNonZeroAddress(_admin); + UtilLib.checkNonZeroAddress(_staderConfig); + UtilLib.checkNonZeroAddress(_ssvNetwork); + UtilLib.checkNonZeroAddress(_ssvNetworkViews); + __AccessControl_init_unchained(); + __Pausable_init(); + __ReentrancyGuard_init(); + staderConfig = IStaderConfig(_staderConfig); + ssvNetwork = ISSVNetwork(_ssvNetwork); + ssvNetworkViews = ISSVNetworkViews(_ssvNetworkViews); + nextOperatorId = 1; + nextValidatorId = 1; + inputKeyCountLimit = 30; + verifiedKeyBatchSize = 50; + batchSizeToRemoveValidatorFromSSV = 10; + batchSizeToRegisterValidatorFromSSV = 10; + _grantRole(DEFAULT_ADMIN_ROLE, _admin); + } + + /** + * @notice white list the permissioned node operator + * @dev only `MANAGER` can call, whitelisting a one way change there is no blacklisting + * @param _permissionedNOs array of permissioned NOs address + */ + function whitelistPermissionedNOs(address[] calldata _permissionedNOs) external { + UtilLib.onlyManagerRole(msg.sender, staderConfig); + uint256 permissionedNosLength = _permissionedNOs.length; + for (uint256 i; i < permissionedNosLength; i++) { + address operator = _permissionedNOs[i]; + UtilLib.checkNonZeroAddress(operator); + permissionList[operator] = true; + emit OperatorWhitelisted(operator); + } + } + + /** + * @notice onboard a node operator + * @param _ssvOperatorID SSV defined ID of the operator + * @param _operatorName name of operator + * @param _operatorRewardAddress eth1 address of operator to get rewards and withdrawals + */ + function onboardNodeOperator( + uint64 _ssvOperatorID, + string calldata _operatorName, + address payable _operatorRewardAddress + ) external whenNotPaused { + address poolUtils = staderConfig.getPoolUtils(); + if (IPoolUtils(poolUtils).poolAddressById(POOL_ID) != staderConfig.getSSVPool()) { + revert DuplicatePoolIDOrPoolNotAdded(); + } + IPoolUtils(poolUtils).onlyValidName(_operatorName); + UtilLib.checkNonZeroAddress(_operatorRewardAddress); + // verify weather operator is onboard with SSV along with being a private and active + // checks if operator has whitelisted this contract address + (address operatorOwner, , , address whitelisted, bool isPrivate, bool isActive) = ssvNetworkViews + .getOperatorById(_ssvOperatorID); + if (msg.sender != operatorOwner || whitelisted != address(this) || isPrivate != true || isActive != true) { + revert CallerFailingSSVOperatorChecks(); + } + //checks if operator already onboarded in any pool of protocol + if (IPoolUtils(poolUtils).isExistingOperator(msg.sender)) { + revert OperatorAlreadyOnBoardedInProtocol(); + } + operatorStructById[nextOperatorId] = SSVOperator( + permissionList[msg.sender], + _operatorName, + _operatorRewardAddress, + msg.sender, + _ssvOperatorID, + 0, + 0 + ); + operatorIDByAddress[msg.sender] = nextOperatorId; + nextOperatorId++; + emit SSVOperatorOnboard(msg.sender, nextOperatorId - 1); + } + + /** + * @notice permissionless NO deposit collateral amount to run validators + * @dev allows only permissionless operator to add collateral + * amount of collateral should be multiple of collateral required per key-share + */ + function depositCollateral() external payable { + uint64 operatorId = operatorIDByAddress[msg.sender]; + _verifyOperatorAndDepositAmount(operatorId, msg.value); + operatorStructById[operatorId].bondAmount += msg.value; + emit BondDeposited(msg.sender, msg.value); + } + + /** + * @notice add validator keys + * @dev only accepts call from stader `OPERATOR` + * @param _pubkey pubkey key of validators + * @param _preDepositSignature signature of a validators for 1ETH deposit + * @param _depositSignature signature of a validator for 31ETH deposit + */ + function addValidatorKeys( + bytes[] calldata _pubkey, + bytes[] calldata _preDepositSignature, + bytes[] calldata _depositSignature + ) external whenNotPaused { + UtilLib.onlyOperatorRole(msg.sender, staderConfig); + uint256 keyCount = _pubkey.length; + _verifyAddValidatorsKeysInputParams(keyCount, _preDepositSignature.length, _depositSignature.length); + address vaultFactory = staderConfig.getVaultFactory(); + address poolUtils = staderConfig.getPoolUtils(); + for (uint256 i; i < keyCount; ) { + IPoolUtils(poolUtils).onlyValidKeys(_pubkey[i], _preDepositSignature[i], _depositSignature[i]); + address withdrawVault = IVaultFactory(vaultFactory).deploySSVValidatorWithdrawalVault( + POOL_ID, + nextValidatorId + ); + validatorRegistry[nextValidatorId] = Validator( + ValidatorStatus.INITIALIZED, + _pubkey[i], + _preDepositSignature[i], + _depositSignature[i], + withdrawVault, + 0, + 0, + 0 + ); + validatorIdByPubkey[_pubkey[i]] = nextValidatorId; + emit AddedValidatorKey(msg.sender, _pubkey[i], nextValidatorId); + nextValidatorId++; + unchecked { + ++i; + } + } + } + + /** + * @notice move validator state from PRE_DEPOSIT to DEPOSIT + * after verifying pre-sign message, front running and deposit signature. + * report front run and invalid signature pubkeys + * @dev only stader oracle contract can call + * @param _readyToDepositPubkey array of pubkeys ready to be moved to DEPOSIT state + * @param _frontRunPubkey array for pubkeys which got front deposit + * @param _invalidSignaturePubkey array of pubkey which has invalid signature for deposit + */ + function markValidatorReadyToDeposit( + bytes[] calldata _readyToDepositPubkey, + bytes[] calldata _frontRunPubkey, + bytes[] calldata _invalidSignaturePubkey + ) external nonReentrant whenNotPaused { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.STADER_ORACLE()); + uint256 readyToDepositValidatorsLength = _readyToDepositPubkey.length; + uint256 frontRunValidatorsLength = _frontRunPubkey.length; + uint256 invalidSignatureValidatorsLength = _invalidSignaturePubkey.length; + + if ( + readyToDepositValidatorsLength + frontRunValidatorsLength + invalidSignatureValidatorsLength > + verifiedKeyBatchSize + ) { + revert TooManyVerifiedKeysReported(); + } + + //handle the front run validators + for (uint256 i; i < frontRunValidatorsLength; ) { + uint256 validatorId = validatorIdByPubkey[_frontRunPubkey[i]]; + // only PRE_DEPOSIT status check will also include validatorId = 0 check + // as status for that will be INITIALIZED(default status) + _onlyPreDepositValidator(validatorId); + validatorRegistry[validatorId].status = ValidatorStatus.FRONT_RUN; + emit ValidatorMarkedAsFrontRunned(_frontRunPubkey[i], validatorId); + unchecked { + ++i; + } + } + + //handle the invalid signature validators + for (uint256 i; i < invalidSignatureValidatorsLength; ) { + uint256 validatorId = validatorIdByPubkey[_invalidSignaturePubkey[i]]; + // only PRE_DEPOSIT status check will also include validatorId = 0 check + // as status for that will be INITIALIZED(default status) + _onlyPreDepositValidator(validatorId); + validatorRegistry[validatorId].status = ValidatorStatus.INVALID_SIGNATURE; + emit ValidatorStatusMarkedAsInvalidSignature(_invalidSignaturePubkey[i], validatorId); + unchecked { + ++i; + } + } + + address ssvPool = staderConfig.getSSVPool(); + uint256 totalDefectedKeys = frontRunValidatorsLength + invalidSignatureValidatorsLength; + if (totalDefectedKeys > 0) { + _decreaseTotalActiveValidatorCount(totalDefectedKeys); + ISSVPool(ssvPool).transferETHOfDefectiveKeysToSSPM(totalDefectedKeys); + } + ISSVPool(ssvPool).fullDepositOnBeaconChain(_readyToDepositPubkey); + } + + /** + * @notice register validator with SSV network + * @param publicKey array of validator public keys + * @param staderOperatorIds array of array of stader operator Ids + * @param sharesData array of sharesData + * @param cluster cluster array + */ + function registerValidatorsWithSSV( + bytes[] calldata publicKey, + uint64[][] memory staderOperatorIds, + bytes[] calldata sharesData, + ISSVNetworkCore.Cluster[] memory cluster + ) external { + UtilLib.onlyOperatorRole(msg.sender, staderConfig); + uint256 keyCount = publicKey.length; + if (keyCount > batchSizeToRegisterValidatorFromSSV) { + revert InputSizeIsMoreThanBatchSize(); + } + if (keyCount != staderOperatorIds.length || keyCount != sharesData.length || keyCount != cluster.length) { + revert MisMatchingInputKeysSize(); + } + for (uint256 i; i < keyCount; ) { + operatorIdssByPubkey[publicKey[i]] = staderOperatorIds[i]; + uint64[] memory ssvOperatorIds = _validateStaderOperators(staderOperatorIds[i]); + ssvNetwork.registerValidator(publicKey[i], ssvOperatorIds, sharesData[i], 0, cluster[i]); + unchecked { + ++i; + } + } + } + + /** + * @notice handling of fully withdrawn validators + * @dev list of pubkeys reported by oracle + * @param _pubkeys array of withdrawn validators pubkey + */ + function withdrawnValidators(bytes[] calldata _pubkeys) external { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.STADER_ORACLE()); + uint256 withdrawnValidatorCount = _pubkeys.length; + if (withdrawnValidatorCount > staderConfig.getWithdrawnKeyBatchSize()) { + revert TooManyWithdrawnKeysReported(); + } + for (uint256 i; i < withdrawnValidatorCount; ) { + uint256 validatorId = validatorIdByPubkey[_pubkeys[i]]; + if (validatorRegistry[validatorId].status != ValidatorStatus.DEPOSITED) { + revert UNEXPECTED_STATUS(); + } + _decreaseOperatorsKeyShareCount(_pubkeys[i]); + validatorRegistry[validatorId].status = ValidatorStatus.WITHDRAWN; + validatorRegistry[validatorId].withdrawnBlock = block.number; + ISSVValidatorWithdrawalVault(validatorRegistry[validatorId].withdrawVaultAddress).settleFunds(); + emit ValidatorWithdrawn(_pubkeys[i], validatorId); + unchecked { + ++i; + } + } + _decreaseTotalActiveValidatorCount(withdrawnValidatorCount); + } + + function removeValidatorFromSSVNetwork(bytes[] calldata publicKey, ISSVNetworkCore.Cluster[] memory cluster) + external + { + UtilLib.onlyOperatorRole(msg.sender, staderConfig); + if (publicKey.length > batchSizeToRemoveValidatorFromSSV) { + revert InputSizeIsMoreThanBatchSize(); + } + for (uint256 i; i < publicKey.length; i++) { + uint256 validatorId = validatorIdByPubkey[publicKey[i]]; + if (validatorRegistry[validatorId].status != ValidatorStatus.WITHDRAWN) { + revert ValidatorNotWithdrawn(); + } + ssvNetwork.removeValidator(publicKey[i], operatorIdssByPubkey[publicKey[i]], cluster[i]); + } + } + + /** + * @notice update the status of a validator to `PRE_DEPOSIT` + * @dev only `SSV_POOL` contract can call + * @param _pubkey pubkey of the validator + */ + function markValidatorStatusAsPreDeposit(bytes calldata _pubkey) external { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.SSV_POOL()); + uint256 validatorId = validatorIdByPubkey[_pubkey]; + validatorRegistry[validatorId].status = ValidatorStatus.PRE_DEPOSIT; + emit MarkedValidatorStatusAsPreDeposit(_pubkey); + } + + // function to set fee recipient address of all validator registered to SSV Network via this contract + function setFeeRecipientAddress() external { + ssvNetwork.setFeeRecipientAddress(staderConfig.getSSVSocializingPool()); + } + + /** + * @notice increase the total active validator count + * @dev only `SSV_POOL` calls it when it does the deposit of 1 ETH for validator + * @param _count count to increase total active validator value + */ + function increaseTotalActiveValidatorCount(uint256 _count) external { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.SSV_POOL()); + totalActiveValidatorCount += _count; + emit IncreasedTotalActiveValidatorCount(totalActiveValidatorCount); + } + + /** + * @notice update the next queued validator index by a count + * @dev accept call from `SSV_POOL` + * @param _nextQueuedValidatorIndex updated next index of queued validator + */ + function updateNextQueuedValidatorIndex(uint256 _nextQueuedValidatorIndex) external { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.SSV_POOL()); + nextQueuedValidatorIndex = _nextQueuedValidatorIndex; + emit UpdatedNextQueuedValidatorIndex(nextQueuedValidatorIndex); + } + + /** + * @notice update maximum key to be added in a batch + * @dev only `OPERATOR` role can call + * @param _inputKeyCountLimit updated maximum key limit in the input + */ + function updateInputKeyCountLimit(uint16 _inputKeyCountLimit) external override { + UtilLib.onlyOperatorRole(msg.sender, staderConfig); + inputKeyCountLimit = _inputKeyCountLimit; + emit UpdatedInputKeyCountLimit(inputKeyCountLimit); + } + + /** + * @notice update the batch size of the keys to remove from SSV Network + * @dev only `OPERATOR` role can call + * @param _batchSizeToRemoveValidatorFromSSV count of pubkey to remove from SSV Network in a batch + */ + function updateBatchSizeToRemoveValidatorFromSSV(uint64 _batchSizeToRemoveValidatorFromSSV) external override { + UtilLib.onlyOperatorRole(msg.sender, staderConfig); + batchSizeToRemoveValidatorFromSSV = _batchSizeToRemoveValidatorFromSSV; + emit UpdatedBatchSizeToRemoveValidatorFromSSV(batchSizeToRemoveValidatorFromSSV); + } + + /** + * @notice update the batch size of the keys to register with SSV Network + * @dev only `OPERATOR` role can call + * @param _batchSizeToRegisterValidatorFromSSV count of pubkey to register with SSV Network in a batch + */ + function updateBatchSizeToRegisterValidatorFromSSV(uint64 _batchSizeToRegisterValidatorFromSSV) external override { + UtilLib.onlyOperatorRole(msg.sender, staderConfig); + batchSizeToRegisterValidatorFromSSV = _batchSizeToRegisterValidatorFromSSV; + emit UpdatedBatchSizeToRegisterValidatorWithSSV(batchSizeToRegisterValidatorFromSSV); + } + + /** + * @notice update the max number of verified validator keys reported by oracle + * @dev only `OPERATOR` can call + * @param _verifiedKeysBatchSize updated maximum verified key limit in the oracle input + */ + function updateVerifiedKeysBatchSize(uint256 _verifiedKeysBatchSize) external { + UtilLib.onlyOperatorRole(msg.sender, staderConfig); + verifiedKeyBatchSize = _verifiedKeysBatchSize; + emit UpdatedVerifiedKeyBatchSize(_verifiedKeysBatchSize); + } + + /** + * @notice update the address of staderConfig + * @dev only `DEFAULT_ADMIN_ROLE` role can update + */ + function updateStaderConfig(address _staderConfig) external onlyRole(DEFAULT_ADMIN_ROLE) { + UtilLib.checkNonZeroAddress(_staderConfig); + staderConfig = IStaderConfig(_staderConfig); + emit UpdatedStaderConfig(_staderConfig); + } + + // check for only PRE_DEPOSIT state validators + function onlyPreDepositValidator(bytes calldata _pubkey) external view { + uint256 validatorId = validatorIdByPubkey[_pubkey]; + _onlyPreDepositValidator(validatorId); + } + + // check for duplicate keys in permissionless node registry + function isExistingPubkey(bytes calldata _pubkey) external view override returns (bool) { + return validatorIdByPubkey[_pubkey] != 0; + } + + // check for duplicate operator in permissionless node registry + function isExistingOperator(address _operAddr) external view override returns (bool) { + return operatorIDByAddress[_operAddr] != 0; + } + + /** + * @notice returns total active validator for SSV pool + * @dev return the variable totalActiveValidatorCount + * @return _validatorCount active validator count + */ + function getTotalActiveValidatorCount() external view returns (uint256) { + return totalActiveValidatorCount; + } + + /** + * @notice returns total queued keys for ssv pool + * @return _validatorCount queued validator count + */ + function getTotalQueuedValidatorCount() external view returns (uint256) { + return nextValidatorId - nextQueuedValidatorIndex; + } + + function getCollateralETH() external pure returns (uint256) { + return COLLATERAL_ETH; + } + + /** + * @notice get the total count of keyshare for an operator + * @param _operatorId Id of node operator + */ + function getOperatorTotalKeys(uint256 _operatorId) public view override returns (uint256 _totalKeys) { + _totalKeys = operatorStructById[uint64(_operatorId)].keyShareCount; + } + + /** + * @notice returns operator key share count, start and end index parameters are not use while computing this + * these params are defined to keep the function signature same as other pools + * pass any number in these params to get the number of key share of the operator + * @param _nodeOperator address of the operator + * @param _startIndex parameter defined to maintain integrity across other pools + * @param _endIndex parameter defined to maintain integrity across other pools + */ + function getOperatorTotalNonTerminalKeys( + address _nodeOperator, + uint256 _startIndex, + uint256 _endIndex + ) external view returns (uint64) { + uint64 operatorId = operatorIDByAddress[_nodeOperator]; + return operatorStructById[operatorId].keyShareCount; + } + + function getOperatorsIdsForValidatorId(uint256 validatorId) external view returns (uint64[] memory) { + return operatorIdssByPubkey[validatorRegistry[validatorId].pubkey]; + } + + /** + * @notice Returns an array of active validators + * + * @param _pageNumber The page number of the results to fetch (starting from 1). + * @param _pageSize The maximum number of items per page. + * + * @return An array of `Validator` objects representing the active validators. + */ + function getAllActiveValidators(uint256 _pageNumber, uint256 _pageSize) external view returns (Validator[] memory) { + if (_pageNumber == 0) { + revert PageNumberIsZero(); + } + uint256 startIndex = (_pageNumber - 1) * _pageSize + 1; + uint256 endIndex = startIndex + _pageSize; + endIndex = endIndex > nextValidatorId ? nextValidatorId : endIndex; + Validator[] memory validators = new Validator[](_pageSize); + uint256 validatorCount; + for (uint256 i = startIndex; i < endIndex; i++) { + if (_isActiveValidator(i)) { + validators[validatorCount] = validatorRegistry[i]; + validatorCount++; + } + } + // If the result array isn't full, resize it to remove the unused elements + assembly { + mstore(validators, validatorCount) + } + + return validators; + } + + // checks if validator is active, + //active validator are those having user deposit staked on beacon chain + function _isActiveValidator(uint256 _validatorId) internal view returns (bool) { + Validator memory validator = validatorRegistry[_validatorId]; + return (validator.status == ValidatorStatus.PRE_DEPOSIT || validator.status == ValidatorStatus.DEPOSITED); + } + + function _verifyOperatorAndDepositAmount(uint64 _operatorId, uint256 _depositAmount) internal view { + if (_operatorId == 0 || operatorStructById[_operatorId].operatorType) { + revert OperatorNotOnboardOrPermissioned(); + } + if (_depositAmount % COLLATERAL_ETH_PER_KEY_SHARE != 0) { + revert InvalidCollateralAmount(); + } + } + + function _verifyAddValidatorsKeysInputParams( + uint256 _keyCount, + uint256 _preDepositSigLen, + uint256 _depositSigLen + ) internal view { + if (_keyCount != _preDepositSigLen || _keyCount != _depositSigLen) { + revert MisMatchingInputKeysSize(); + } + if (_keyCount == 0 || _keyCount > inputKeyCountLimit) { + revert InvalidKeyCount(); + } + } + + function _validateStaderOperators(uint64[] memory staderOperatorIds) + internal + returns (uint64[] memory ssvOperatorIds) + { + if (staderOperatorIds.length != CLUSTER_SIZE) { + revert DifferentClusterSize(); + } + ssvOperatorIds = new uint64[](CLUSTER_SIZE); + uint256 totalCollateral; + for (uint64 j = 0; j < CLUSTER_SIZE; j++) { + SSVOperator storage operator = operatorStructById[staderOperatorIds[j]]; + bool enoughSDCollateral = ISDCollateral(staderConfig.getSDCollateral()).hasEnoughSDCollateral( + msg.sender, + POOL_ID, + operator.keyShareCount + 1 + ); + if (!operator.operatorType && (operator.bondAmount < COLLATERAL_ETH_PER_KEY_SHARE || !enoughSDCollateral)) { + revert NotSufficientCollateralPerKeyShare(); + } + if (!operator.operatorType) { + totalCollateral += COLLATERAL_ETH_PER_KEY_SHARE; + } + operator.bondAmount -= COLLATERAL_ETH_PER_KEY_SHARE; + operator.keyShareCount += 1; + ssvOperatorIds[j] = operatorStructById[staderOperatorIds[j]].operatorSSVID; + } + if (totalCollateral != COLLATERAL_ETH) { + revert NotSufficientCollateralPerValidator(); + } + } + + function _onlyPreDepositValidator(uint256 _validatorId) internal view { + if (validatorRegistry[_validatorId].status != ValidatorStatus.PRE_DEPOSIT) { + revert UNEXPECTED_STATUS(); + } + } + + // decreases the pool total active validator count + function _decreaseTotalActiveValidatorCount(uint256 _count) internal { + totalActiveValidatorCount -= _count; + emit DecreasedTotalActiveValidatorCount(totalActiveValidatorCount); + } + + function _decreaseOperatorsKeyShareCount(bytes calldata pubkey) internal { + uint64[] memory operatorIds = operatorIdssByPubkey[pubkey]; + for (uint256 j; j < operatorIds.length; ) { + operatorStructById[operatorIds[j]].keyShareCount -= 1; + unchecked { + ++j; + } + } + } +} diff --git a/contracts/DVT/SSV/SSVPool.sol b/contracts/DVT/SSV/SSVPool.sol new file mode 100644 index 00000000..b4ad7bd6 --- /dev/null +++ b/contracts/DVT/SSV/SSVPool.sol @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import '../../library/UtilLib.sol'; + +import '../../library/ValidatorStatus.sol'; + +import '../../interfaces/IStaderConfig.sol'; +import '../../interfaces/IVaultFactory.sol'; +import '../../interfaces/IDepositContract.sol'; +import '../../interfaces/DVT/SSV/ISSVPool.sol'; +import '../../interfaces/IStaderInsuranceFund.sol'; +import '../../interfaces/IStaderStakePoolManager.sol'; +import '../../interfaces/DVT/SSV/ISSVNodeRegistry.sol'; + +import '@openzeppelin/contracts/utils/math/Math.sol'; +import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol'; + +contract SSVPool is ISSVPool, AccessControlUpgradeable, ReentrancyGuardUpgradeable { + using Math for uint256; + + IStaderConfig public staderConfig; + // @inheritdoc IStaderPoolBase + uint256 public override protocolFee; + + // @inheritdoc IStaderPoolBase + uint256 public override operatorFee; + + uint256 public preDepositValidatorCount; + + uint256 public constant MAX_COMMISSION_LIMIT_BIPS = 1500; + + uint256 public constant DEPOSIT_NODE_BOND = 1.6 ether; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(address _admin, address _staderConfig) external initializer { + UtilLib.checkNonZeroAddress(_admin); + UtilLib.checkNonZeroAddress(_staderConfig); + __AccessControl_init_unchained(); + __ReentrancyGuard_init(); + protocolFee = 500; + operatorFee = 500; + staderConfig = IStaderConfig(_staderConfig); + _grantRole(DEFAULT_ADMIN_ROLE, _admin); + } + + function receiveInsuranceFund() external payable { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.STADER_INSURANCE_FUND()); + emit ReceivedInsuranceFund(msg.value); + } + + // transfer the 32ETH for defective keys (front run, invalid signature) to stader stake pool manager (SSPM) + function transferETHOfDefectiveKeysToSSPM(uint256 _defectiveKeyCount) external nonReentrant { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.SSV_NODE_REGISTRY()); + //decrease the preDeposit validator count + _decreasePreDepositValidatorCount(_defectiveKeyCount); + //get 1ETH from insurance fund + IStaderInsuranceFund(staderConfig.getStaderInsuranceFund()).reimburseUserFund( + _defectiveKeyCount * staderConfig.getPreDepositSize() + ); + // send back 30.4 ETH (32 - collateral) for front run and invalid signature validators back to pool manager + // These counts are correct because any double reporting of frontrun/invalid statuses results in an error. + uint256 amountToSendToPoolManager = _defectiveKeyCount * + (staderConfig.getStakedEthPerNode() - DEPOSIT_NODE_BOND); + //slither-disable-next-line arbitrary-send-eth + IStaderStakePoolManager(staderConfig.getStakePoolManager()).receiveExcessEthFromPool{ + value: amountToSendToPoolManager + }(INodeRegistry((staderConfig).getSSVNodeRegistry()).POOL_ID()); + emit TransferredETHToSSPMForDefectiveKeys(amountToSendToPoolManager); + } + + /** + * @notice receives eth from pool manager to deposit for validators on beacon chain + * @dev deposit PRE_DEPOSIT_SIZE of ETH for validators while adhering to pool capacity. + */ + function stakeUserETHToBeaconChain() external payable override nonReentrant { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.STAKE_POOL_MANAGER()); + uint256 requiredValidators = msg.value / (staderConfig.getStakedEthPerNode() - DEPOSIT_NODE_BOND); + address nodeRegistryAddress = staderConfig.getSSVNodeRegistry(); + + address vaultFactoryAddress = staderConfig.getVaultFactory(); + address ethDepositContract = staderConfig.getETHDepositContract(); + uint256 depositQueueStartIndex = ISSVNodeRegistry(nodeRegistryAddress).nextQueuedValidatorIndex(); + for (uint256 i = depositQueueStartIndex; i < requiredValidators + depositQueueStartIndex; ) { + _preDepositOnBeaconChain(nodeRegistryAddress, vaultFactoryAddress, ethDepositContract, i); + unchecked { + ++i; + } + } + ISSVNodeRegistry(nodeRegistryAddress).updateNextQueuedValidatorIndex( + depositQueueStartIndex + requiredValidators + ); + _increasePreDepositValidatorCount(requiredValidators); + ISSVNodeRegistry(nodeRegistryAddress).increaseTotalActiveValidatorCount(requiredValidators); + } + + // deposit `FULL_DEPOSIT_SIZE` for the verified preDeposited Validator + function fullDepositOnBeaconChain(bytes[] calldata _pubkey) external nonReentrant { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.SSV_NODE_REGISTRY()); + address nodeRegistryAddress = msg.sender; + address vaultFactory = staderConfig.getVaultFactory(); + address ethDepositContract = staderConfig.getETHDepositContract(); + uint256 pubkeyCount = _pubkey.length; + //decrease the preDeposit validator count + _decreasePreDepositValidatorCount(pubkeyCount); + for (uint256 i; i < pubkeyCount; ) { + ISSVNodeRegistry(nodeRegistryAddress).onlyPreDepositValidator(_pubkey[i]); + uint256 validatorId = INodeRegistry(nodeRegistryAddress).validatorIdByPubkey(_pubkey[i]); + (, , , bytes memory depositSignature, address withdrawVaultAddress, , , ) = INodeRegistry( + nodeRegistryAddress + ).validatorRegistry(validatorId); + bytes memory withdrawCredential = IVaultFactory(vaultFactory).getValidatorWithdrawCredential( + withdrawVaultAddress + ); + uint256 fullDepositSize = staderConfig.getFullDepositSize(); + bytes32 depositDataRoot = this.computeDepositDataRoot( + _pubkey[i], + depositSignature, + withdrawCredential, + fullDepositSize + ); + + //slither-disable-next-line arbitrary-send-eth + IDepositContract(ethDepositContract).deposit{value: fullDepositSize}( + _pubkey[i], + withdrawCredential, + depositSignature, + depositDataRoot + ); + // ISSVNodeRegistry(nodeRegistryAddress).updateDepositStatusAndBlock(validatorId); + emit ValidatorDepositedOnBeaconChain(validatorId, _pubkey[i]); + unchecked { + ++i; + } + } + } + + /** + * @notice transfer the excess ETH sent by some EAO or non stader contract back to SSPM + * @dev preDepositValidatorCount has to be 0 for determining excess ETH value + */ + function transferExcessETHToSSPM() external nonReentrant { + if (preDepositValidatorCount != 0 || address(this).balance == 0) { + revert CouldNotDetermineExcessETH(); + } + IStaderStakePoolManager(staderConfig.getStakePoolManager()).receiveExcessEthFromPool{ + value: address(this).balance + }(INodeRegistry((staderConfig).getSSVNodeRegistry()).POOL_ID()); + } + + function getNodeRegistry() external view override returns (address) { + return staderConfig.getSSVNodeRegistry(); + } + + // @inheritdoc IStaderPoolBase + function getSocializingPoolAddress() external view returns (address) { + return staderConfig.getSSVSocializingPool(); + } + + // @inheritdoc IStaderPoolBase + function setCommissionFees(uint256 _protocolFee, uint256 _operatorFee) external { + UtilLib.onlyManagerRole(msg.sender, staderConfig); + if (_protocolFee + _operatorFee > MAX_COMMISSION_LIMIT_BIPS) { + revert InvalidCommission(); + } + protocolFee = _protocolFee; + operatorFee = _operatorFee; + + emit UpdatedCommissionFees(_protocolFee, _operatorFee); + } + + //update the address of staderConfig + function updateStaderConfig(address _staderConfig) external onlyRole(DEFAULT_ADMIN_ROLE) { + UtilLib.checkNonZeroAddress(_staderConfig); + staderConfig = IStaderConfig(_staderConfig); + emit UpdatedStaderConfig(_staderConfig); + } + + // @notice calculate the deposit data root based on pubkey, signature, withdrawCredential and amount + // formula based on ethereum deposit contract + function computeDepositDataRoot( + bytes calldata _pubkey, + bytes calldata _signature, + bytes calldata _withdrawCredential, + uint256 _depositAmount + ) external pure returns (bytes32) { + bytes memory amount = to_little_endian_64(_depositAmount); + bytes32 pubkey_root = sha256(abi.encodePacked(_pubkey, bytes16(0))); + bytes32 signature_root = sha256( + abi.encodePacked( + sha256(abi.encodePacked(_signature[:64])), + sha256(abi.encodePacked(_signature[64:], bytes32(0))) + ) + ); + return + sha256( + abi.encodePacked( + sha256(abi.encodePacked(pubkey_root, _withdrawCredential)), + sha256(abi.encodePacked(amount, bytes24(0), signature_root)) + ) + ); + } + + function _increasePreDepositValidatorCount(uint256 _count) internal { + preDepositValidatorCount += _count; + } + + function _decreasePreDepositValidatorCount(uint256 _count) internal { + preDepositValidatorCount -= _count; + } + + // deposit `PRE_DEPOSIT_SIZE` for validator + function _preDepositOnBeaconChain( + address _nodeRegistryAddress, + address _vaultFactory, + address _ethDepositContract, + uint256 _validatorId + ) internal { + (, bytes memory pubkey, bytes memory preDepositSignature, , address withdrawVaultAddress, , , ) = INodeRegistry( + _nodeRegistryAddress + ).validatorRegistry(_validatorId); + + bytes memory withdrawCredential = IVaultFactory(_vaultFactory).getValidatorWithdrawCredential( + withdrawVaultAddress + ); + uint256 preDepositSize = staderConfig.getPreDepositSize(); + bytes32 depositDataRoot = this.computeDepositDataRoot( + pubkey, + preDepositSignature, + withdrawCredential, + preDepositSize + ); + + //slither-disable-next-line arbitrary-send-eth + IDepositContract(_ethDepositContract).deposit{value: preDepositSize}( + pubkey, + withdrawCredential, + preDepositSignature, + depositDataRoot + ); + ISSVNodeRegistry(_nodeRegistryAddress).markValidatorStatusAsPreDeposit(pubkey); + emit ValidatorPreDepositedOnBeaconChain(pubkey); + } + + //ethereum deposit contract function to get amount into little_endian_64 + function to_little_endian_64(uint256 _depositAmount) internal pure returns (bytes memory ret) { + uint64 value = uint64(_depositAmount / 1 gwei); + + ret = new bytes(8); + bytes8 bytesValue = bytes8(value); + // Byteswapping during copying to bytes. + ret[0] = bytesValue[7]; + ret[1] = bytesValue[6]; + ret[2] = bytesValue[5]; + ret[3] = bytesValue[4]; + ret[4] = bytesValue[3]; + ret[5] = bytesValue[2]; + ret[6] = bytesValue[1]; + ret[7] = bytesValue[0]; + } +} diff --git a/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol b/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol new file mode 100644 index 00000000..b1cf5113 --- /dev/null +++ b/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import '../../library/UtilLib.sol'; +import './SSVVaultProxy.sol'; +import '../../interfaces/IPenalty.sol'; +import '../../interfaces/IPoolUtils.sol'; +import '../../interfaces/IStaderStakePoolManager.sol'; +import '../../interfaces/DVT/SSV/ISSVNodeRegistry.sol'; +import '../../interfaces/IOperatorRewardsCollector.sol'; +import '../../interfaces/SDCollateral/ISDCollateral.sol'; +import '../../interfaces/DVT/SSV/ISSVValidatorWithdrawalVault.sol'; + +import '@openzeppelin/contracts/utils/math/Math.sol'; + +contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { + bool internal vaultSettleStatus; + using Math for uint256; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() {} + + // Allows the contract to receive ETH + receive() external payable { + emit ETHReceived(msg.sender, msg.value); + } + + function distributeRewards() external override { + uint8 poolId = SSVVaultProxy(payable(address(this))).poolId(); + uint256 validatorId = SSVVaultProxy(payable(address(this))).validatorId(); + IStaderConfig staderConfig = SSVVaultProxy(payable(address(this))).staderConfig(); + ISSVNodeRegistry nodeRegistry = ISSVNodeRegistry( + IPoolUtils(staderConfig.getPoolUtils()).getNodeRegistry(poolId) + ); + + uint256 totalRewards = address(this).balance; + if (!staderConfig.onlyManagerRole(msg.sender) && totalRewards > staderConfig.getRewardsThreshold()) { + emit DistributeRewardFailed(totalRewards, staderConfig.getRewardsThreshold()); + revert InvalidRewardAmount(); + } + if (totalRewards == 0) { + revert NotEnoughRewardToDistribute(); + } + (uint256 userShare, uint256 operatorShare, uint256 protocolShare) = IPoolUtils(staderConfig.getPoolUtils()) + .calculateRewardShare(poolId, totalRewards); + + //TODO how to make sure only 4 operators + //TODO pull this number 4 from somewhere maybe ssv node registry + //TODO add documentation for this formula + // Distribute rewards + uint64[] memory operatorIds = nodeRegistry.getOperatorsIdsForValidatorId(validatorId); + uint256 totalOperators = operatorIds.length; + for (uint8 i; i < totalOperators; ) { + (bool operatorType, , , address operatorAddress, , , ) = nodeRegistry.operatorStructById(operatorIds[i]); + uint256 operatorKeyLevelShare; + if (operatorType) { + operatorKeyLevelShare = getPermissionedOperatorShare( + operatorShare, + totalRewards, + nodeRegistry.getCollateralETH(), + staderConfig.getStakedEthPerNode() + ); + } else { + operatorKeyLevelShare = getPermissionlessOperatorShare( + operatorShare, + totalRewards, + nodeRegistry.getCollateralETH(), + staderConfig.getStakedEthPerNode() + ); + } + IOperatorRewardsCollector(staderConfig.getOperatorRewardsCollector()).depositFor{ + value: operatorKeyLevelShare + }(operatorAddress); + unchecked { + i++; + } + } + IStaderStakePoolManager(staderConfig.getStakePoolManager()).receiveWithdrawVaultUserShare{value: userShare}(); + UtilLib.sendValue(payable(staderConfig.getStaderTreasury()), protocolShare); + emit DistributedRewards(userShare, operatorShare, protocolShare); + } + + function settleFunds() external override { + uint8 poolId = SSVVaultProxy(payable(address(this))).poolId(); + uint256 validatorId = SSVVaultProxy(payable(address(this))).validatorId(); + IStaderConfig staderConfig = SSVVaultProxy(payable(address(this))).staderConfig(); + if (msg.sender != IPoolUtils(staderConfig.getPoolUtils()).getNodeRegistry(poolId)) { + revert CallerNotNodeRegistryContract(); + } + uint64[] memory operatorIds = ISSVNodeRegistry(msg.sender).getOperatorsIdsForValidatorId(validatorId); + uint256 totalOperators = operatorIds.length; + + (uint256 userSharePrelim, uint256 operatorShare, uint256 protocolShare) = calculateValidatorWithdrawalShare( + poolId, + staderConfig + ); + + uint256 penaltyAmount = getUpdatedPenaltyAmount(validatorId, staderConfig); + uint256 permissionedOperatorShare; + uint256 permissionlessOperatorShare; + uint256 collateralETH = ISSVNodeRegistry(msg.sender).getCollateralETH(); + if (operatorShare <= collateralETH) { + permissionlessOperatorShare = operatorShare / 2; + } else { + uint256 rewards = address(this).balance - staderConfig.getStakedEthPerNode(); + permissionedOperatorShare = getPermissionedOperatorShare( + operatorShare - collateralETH, + rewards, + collateralETH, + staderConfig.getStakedEthPerNode() + ); + + permissionlessOperatorShare = + collateralETH / + 2 + + getPermissionlessOperatorShare( + operatorShare - collateralETH, + rewards, + collateralETH, + staderConfig.getStakedEthPerNode() + ); + } + + if (operatorShare < penaltyAmount) { + //slash SD of permissionless operators + ISDCollateral(staderConfig.getSDCollateral()).slashSSVOperatorSD(poolId, validatorId, operatorIds); + penaltyAmount = operatorShare; + } + uint256 userShare = userSharePrelim + penaltyAmount; + + if (penaltyAmount >= 4 * permissionedOperatorShare) { + permissionlessOperatorShare -= (penaltyAmount - 2 * permissionedOperatorShare) / 2; + permissionedOperatorShare = 0; + } else { + permissionedOperatorShare -= penaltyAmount / 4; + permissionlessOperatorShare -= penaltyAmount / 4; + } + + // Final settlement + vaultSettleStatus = true; + IPenalty(staderConfig.getPenaltyContract()).markValidatorSettled(poolId, validatorId); + IStaderStakePoolManager(staderConfig.getStakePoolManager()).receiveWithdrawVaultUserShare{value: userShare}(); + UtilLib.sendValue(payable(staderConfig.getStaderTreasury()), protocolShare); + for (uint8 i; i < totalOperators; i++) { + (bool operatorType, , , address operatorAddress, , , ) = ISSVNodeRegistry(msg.sender).operatorStructById( + operatorIds[i] + ); + if (operatorType) { + IOperatorRewardsCollector(staderConfig.getOperatorRewardsCollector()).depositFor{ + value: permissionedOperatorShare + }(operatorAddress); + } else { + IOperatorRewardsCollector(staderConfig.getOperatorRewardsCollector()).depositFor{ + value: permissionlessOperatorShare + }(operatorAddress); + } + } + emit SettledFunds(userShare, operatorShare, protocolShare); + } + + function calculateValidatorWithdrawalShare(uint8 _poolId, IStaderConfig _staderConfig) + public + view + returns ( + uint256 userShare, + uint256 operatorShare, + uint256 protocolShare + ) + { + uint256 TOTAL_STAKED_ETH = _staderConfig.getStakedEthPerNode(); + uint256 collateralETH = ISSVNodeRegistry(msg.sender).getCollateralETH(); + uint256 usersETH = TOTAL_STAKED_ETH - collateralETH; + uint256 contractBalance = address(this).balance; + + uint256 totalRewards; + + if (contractBalance <= usersETH) { + userShare = contractBalance; + return (userShare, operatorShare, protocolShare); + } else if (contractBalance <= TOTAL_STAKED_ETH) { + userShare = usersETH; + operatorShare = contractBalance - userShare; + return (userShare, operatorShare, protocolShare); + } else { + totalRewards = contractBalance - TOTAL_STAKED_ETH; + operatorShare = collateralETH; + userShare = usersETH; + } + if (totalRewards > 0) { + (uint256 userReward, uint256 operatorReward, uint256 protocolReward) = IPoolUtils( + _staderConfig.getPoolUtils() + ).calculateRewardShare(_poolId, totalRewards); + userShare += userReward; + operatorShare += operatorReward; + protocolShare += protocolReward; + } + } + + // HELPER METHODS + + function getUpdatedPenaltyAmount(uint256 _validatorId, IStaderConfig _staderConfig) internal returns (uint256) { + (, bytes memory pubkey, , , , , , ) = ISSVNodeRegistry(msg.sender).validatorRegistry(_validatorId); + bytes[] memory pubkeyArray = new bytes[](1); + pubkeyArray[0] = pubkey; + IPenalty(_staderConfig.getPenaltyContract()).updateTotalPenaltyAmount(pubkeyArray); + return IPenalty(_staderConfig.getPenaltyContract()).totalPenaltyAmount(pubkey); + } + + function getPermissionedOperatorShare( + uint256 operatorShare, + uint256 rewards, + uint256 collateralETH, + uint256 ETH_PER_NODE + ) internal pure returns (uint256) { + return (operatorShare - (rewards * collateralETH) / ETH_PER_NODE) / 4; + } + + function getPermissionlessOperatorShare( + uint256 operatorShare, + uint256 rewards, + uint256 collateralETH, + uint256 ETH_PER_NODE + ) internal pure returns (uint256) { + return (operatorShare + (rewards * collateralETH) / ETH_PER_NODE) / 4; + } +} diff --git a/contracts/DVT/SSV/SSVVaultProxy.sol b/contracts/DVT/SSV/SSVVaultProxy.sol new file mode 100644 index 00000000..022f2b94 --- /dev/null +++ b/contracts/DVT/SSV/SSVVaultProxy.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import '../../library/UtilLib.sol'; +import '../../interfaces/DVT/SSV/ISSVVaultProxy.sol'; + +//contract to delegate call to ssv validator withdraw vault implementation +contract SSVVaultProxy is ISSVVaultProxy { + bool public override vaultSettleStatus; + bool public override isInitialized; + uint8 public override poolId; + uint256 public override validatorId; + address public override owner; + IStaderConfig public override staderConfig; + + constructor() { + isInitialized = true; + } + + //initialise the vault proxy with data + function initialise( + uint8 _poolId, + uint256 _validatorId, + address _staderConfig + ) external { + if (isInitialized) { + revert AlreadyInitialized(); + } + UtilLib.checkNonZeroAddress(_staderConfig); + isInitialized = true; + poolId = _poolId; + validatorId = _validatorId; + staderConfig = IStaderConfig(_staderConfig); + owner = staderConfig.getAdmin(); + UtilLib.checkNonZeroAddress(owner); + } + + /**route all call to this proxy contract to the latest SSV validator withdrawl vault contract + * fetched from staderConfig. This approach will help in changing the implementation + * of ssv validator's withdrawal vault for already deployed vaults*/ + fallback(bytes calldata _input) external payable returns (bytes memory) { + (bool success, bytes memory data) = (staderConfig.getSSVValidatorWithdrawalVault()).delegatecall(_input); + if (!success) { + revert(string(data)); + } + return data; + } + + /** + * @notice update the address of stader config contract + * @dev only owner can call + * @param _staderConfig address of updated staderConfig + */ + function updateStaderConfig(address _staderConfig) external override onlyOwner { + UtilLib.checkNonZeroAddress(_staderConfig); + staderConfig = IStaderConfig(_staderConfig); + emit UpdatedStaderConfig(_staderConfig); + } + + // update the owner of vault proxy contract to staderConfig Admin + function updateOwner() external override { + owner = staderConfig.getAdmin(); + emit UpdatedOwner(owner); + } + + //modifier to check only owner + modifier onlyOwner() { + if (msg.sender != owner) { + revert CallerNotOwner(); + } + _; + } +} diff --git a/contracts/SDCollateral.sol b/contracts/SDCollateral.sol index 8024698b..ff59c0cd 100644 --- a/contracts/SDCollateral.sol +++ b/contracts/SDCollateral.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.16; import './library/UtilLib.sol'; import '../contracts/interfaces/IPoolUtils.sol'; +import '../contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol'; import '../contracts/interfaces/SDCollateral/ISDCollateral.sol'; import '../contracts/interfaces/SDCollateral/IAuction.sol'; import '../contracts/interfaces/IStaderOracle.sol'; @@ -80,6 +81,32 @@ contract SDCollateral is ISDCollateral, AccessControlUpgradeable, ReentrancyGuar slashSD(operator, sdToSlash); } + /// @notice slashes one validator equi. SD amount + /// @dev callable only by respective withdrawVaults + /// @param _validatorId validator SD collateral to slash + function slashSSVOperatorSD( + uint8 _poolId, + uint256 _validatorId, + uint64[] memory operatorIds + ) external override nonReentrant { + (, , , , address withdrawVaultAddress, , , ) = INodeRegistry( + IPoolUtils(staderConfig.getPoolUtils()).getNodeRegistry(_poolId) + ).validatorRegistry(_validatorId); + if (msg.sender != withdrawVaultAddress) { + revert CallerNotWithdrawVault(); + } + isPoolThresholdValid(_poolId); + uint256 sdToSlash = convertETHToSD(poolThresholdbyPoolId[_poolId].minThreshold); + for (uint8 i; i < operatorIds.length; i++) { + (bool operatorType, , , address operatorAddress, , , ) = ISSVNodeRegistry(msg.sender).operatorStructById( + operatorIds[i] + ); + if (!operatorType) { + slashSD(operatorAddress, convertETHToSD(sdToSlash)); + } + } + } + /// @notice used to slash operator SD, incase of operator default /// @dev do provide SD approval to auction contract using `maxApproveSD()` /// @param _operator which operator SD collateral to slash diff --git a/contracts/StaderConfig.sol b/contracts/StaderConfig.sol index f7ee8992..de087ae0 100644 --- a/contracts/StaderConfig.sol +++ b/contracts/StaderConfig.sol @@ -77,6 +77,12 @@ contract StaderConfig is IStaderConfig, AccessControlUpgradeable { mapping(bytes32 => address) private contractsMap; mapping(bytes32 => address) private tokensMap; + //SSV Pool + bytes32 public constant override SSV_POOL = keccak256('SSV_POOL'); + bytes32 public constant override SSV_NODE_REGISTRY = keccak256('SSV_NODE_REGISTRY'); + bytes32 public constant override SSV_SOCIALIZING_POOL = keccak256('SSV_SOCIALIZING_POOL'); + bytes32 public constant override SSV_VALIDATOR_WITHDRAWAL_VAULT = keccak256('SSV_VALIDATOR_WITHDRAWAL_VAULT'); + /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); @@ -292,6 +298,25 @@ contract StaderConfig is IStaderConfig, AccessControlUpgradeable { setToken(ETHx, _ethX); } + function updateSSVPool(address _ssvPool) external onlyRole(DEFAULT_ADMIN_ROLE) { + setContract(SSV_POOL, _ssvPool); + } + + function updateSSVNodeRegistry(address _ssvNodeRegistry) external onlyRole(DEFAULT_ADMIN_ROLE) { + setContract(SSV_NODE_REGISTRY, _ssvNodeRegistry); + } + + function updateSSVSocializingPool(address _ssvSocializingPool) external onlyRole(DEFAULT_ADMIN_ROLE) { + setContract(SSV_SOCIALIZING_POOL, _ssvSocializingPool); + } + + function updateSSVValidatorWithdrawalVault(address _ssvValidatorWithdrawalVault) + external + onlyRole(DEFAULT_ADMIN_ROLE) + { + setContract(SSV_VALIDATOR_WITHDRAWAL_VAULT, _ssvValidatorWithdrawalVault); + } + //Constants Getters function getStakedEthPerNode() external view override returns (uint256) { @@ -448,6 +473,22 @@ contract StaderConfig is IStaderConfig, AccessControlUpgradeable { return contractsMap[VALIDATOR_WITHDRAWAL_VAULT_IMPLEMENTATION]; } + function getSSVPool() external view override returns (address) { + return contractsMap[SSV_POOL]; + } + + function getSSVNodeRegistry() external view override returns (address) { + return contractsMap[SSV_NODE_REGISTRY]; + } + + function getSSVSocializingPool() external view override returns (address) { + return contractsMap[SSV_SOCIALIZING_POOL]; + } + + function getSSVValidatorWithdrawalVault() external view override returns (address) { + return contractsMap[SSV_VALIDATOR_WITHDRAWAL_VAULT]; + } + //POR Feed Proxy Getters function getETHBalancePORFeedProxy() external view override returns (address) { return contractsMap[ETH_BALANCE_POR_FEED]; diff --git a/contracts/StaderInsuranceFund.sol b/contracts/StaderInsuranceFund.sol index 867cad2a..d260f142 100644 --- a/contracts/StaderInsuranceFund.sol +++ b/contracts/StaderInsuranceFund.sol @@ -48,16 +48,18 @@ contract StaderInsuranceFund is IStaderInsuranceFund, AccessControlUpgradeable, } /** - * @notice reimburse 1 ETH per key to SSPM for permissioned NOs doing front running or giving invalid signature - * @dev only permissioned pool can call - * @param _amount amount of ETH to transfer to permissioned pool + * @notice reimburse 1 ETH per key to SSPM for permissioned/SSV NOs doing front running or giving invalid signature + * @dev only permissioned/SSV pool can call + * @param _amount amount of ETH to transfer to permissioned/SSV pool */ function reimburseUserFund(uint256 _amount) external override nonReentrant { - UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.PERMISSIONED_POOL()); + if (msg.sender != staderConfig.getPermissionedPool() || msg.sender != staderConfig.getSSVPool()) { + revert InvalidPoolToReimburse(); + } if (address(this).balance < _amount) { revert InSufficientBalance(); } - IPermissionedPool(staderConfig.getPermissionedPool()).receiveInsuranceFund{value: _amount}(); + IPermissionedPool(msg.sender).receiveInsuranceFund{value: _amount}(); } //update the address of staderConfig diff --git a/contracts/factory/VaultFactory.sol b/contracts/factory/VaultFactory.sol index 8fa352ca..66dd1364 100644 --- a/contracts/factory/VaultFactory.sol +++ b/contracts/factory/VaultFactory.sol @@ -2,7 +2,9 @@ pragma solidity 0.8.16; import '../library/UtilLib.sol'; + import '../VaultProxy.sol'; +import '../DVT/SSV/SSVVaultProxy.sol'; import '../interfaces/IVaultFactory.sol'; import '../interfaces/IStaderConfig.sol'; @@ -15,6 +17,8 @@ contract VaultFactory is IVaultFactory, AccessControlUpgradeable { bytes32 public constant override NODE_REGISTRY_CONTRACT = keccak256('NODE_REGISTRY_CONTRACT'); + address public ssvVaultProxyImplementation; + /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); @@ -45,6 +49,20 @@ contract VaultFactory is IVaultFactory, AccessControlUpgradeable { return withdrawVaultAddress; } + function deploySSVValidatorWithdrawalVault(uint8 _poolId, uint256 _validatorId) + external + override + onlyRole(NODE_REGISTRY_CONTRACT) + returns (address) + { + bytes32 salt = sha256(abi.encode(_poolId, _validatorId)); + address withdrawVaultAddress = ClonesUpgradeable.cloneDeterministic(ssvVaultProxyImplementation, salt); + SSVVaultProxy(payable(withdrawVaultAddress)).initialise(_poolId, _validatorId, address(staderConfig)); + + emit SSVValidatorWithdrawalVaultCreated(_validatorId, withdrawVaultAddress); + return withdrawVaultAddress; + } + function deployNodeELRewardVault(uint8 _poolId, uint256 _operatorId) external override @@ -68,6 +86,16 @@ contract VaultFactory is IVaultFactory, AccessControlUpgradeable { return ClonesUpgradeable.predictDeterministicAddress(vaultProxyImplementation, salt); } + function computeSSVValidatorWithdrawalVaultAddress(uint8 _poolId, uint256 _validatorId) + external + view + override + returns (address) + { + bytes32 salt = sha256(abi.encode(_poolId, _validatorId)); + return ClonesUpgradeable.predictDeterministicAddress(vaultProxyImplementation, salt); + } + function computeNodeELRewardVaultAddress(uint8 _poolId, uint256 _operatorId) external view @@ -95,4 +123,14 @@ contract VaultFactory is IVaultFactory, AccessControlUpgradeable { vaultProxyImplementation = _vaultProxyImpl; emit UpdatedVaultProxyImplementation(vaultProxyImplementation); } + + function updateSSVVaultProxyImplementation(address _ssvVaultProxyImpl) + external + override + onlyRole(DEFAULT_ADMIN_ROLE) + { + UtilLib.checkNonZeroAddress(_ssvVaultProxyImpl); + ssvVaultProxyImplementation = _ssvVaultProxyImpl; + emit UpdatedSSVVaultProxyImplementation(ssvVaultProxyImplementation); + } } diff --git a/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol b/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol new file mode 100644 index 00000000..be1ed201 --- /dev/null +++ b/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import '../../INodeRegistry.sol'; + +import '../../../library/ValidatorStatus.sol'; + +struct SSVOperator { + bool operatorType; // 0 for permissionless and 1 for permissioned + string operatorName; // name of the operator + address payable operatorRewardAddress; //Eth1 address of node for reward + address operatorAddress; // address of operator to interact with stader + uint64 operatorSSVID; // operator ID on SSV Network + uint64 keyShareCount; // count of key-share operator is running + uint256 bondAmount; // amount of ETH bond for new key shares +} + +interface ISSVNodeRegistry { + error InvalidKeyCount(); + error PageNumberIsZero(); + error UNEXPECTED_STATUS(); + error InvalidBondAmount(); + error DifferentClusterSize(); + error ValidatorNotWithdrawn(); + error InvalidCollateralAmount(); + error MisMatchingInputKeysSize(); + error TooManyVerifiedKeysReported(); + error InputSizeIsMoreThanBatchSize(); + error TooManyWithdrawnKeysReported(); + error CallerFailingSSVOperatorChecks(); + error DuplicatePoolIDOrPoolNotAdded(); + error OperatorNotOnboardOrPermissioned(); + error OperatorAlreadyOnBoardedInProtocol(); + error NotSufficientCollateralPerKeyShare(); + error NotSufficientCollateralPerValidator(); + + event OperatorWhitelisted(address operator); + event UpdatedStaderConfig(address staderConfig); + event MarkedValidatorStatusAsPreDeposit(bytes pubkey); + event UpdatedInputKeyCountLimit(uint256 inputKeyCountLimit); + event ValidatorWithdrawn(bytes pubkey, uint256 validatorId); + event SSVOperatorOnboard(address operator, uint256 operatorId); + event UpdatedVerifiedKeyBatchSize(uint256 verifiedKeysBatchSize); + event BondDeposited(address operator, uint256 depositBondAmount); + event ValidatorMarkedAsFrontRunned(bytes pubkey, uint256 validatorId); + event UpdatedNextQueuedValidatorIndex(uint256 nextQueuedValidatorIndex); + event IncreasedTotalActiveValidatorCount(uint256 totalActiveValidatorCount); + event DecreasedTotalActiveValidatorCount(uint256 totalActiveValidatorCount); + event ValidatorStatusMarkedAsInvalidSignature(bytes pubkey, uint256 validatorId); + event AddedValidatorKey(address indexed nodeOperator, bytes pubkey, uint256 validatorId); + event UpdatedBatchSizeToRemoveValidatorFromSSV(uint64 batchSizeToRemoveValidatorFromSSV); + event UpdatedBatchSizeToRegisterValidatorWithSSV(uint64 batchSizeToRegisterValidatorFromSSV); + + // function withdrawnValidators(bytes[] calldata _pubkeys) external; + + function markValidatorReadyToDeposit( + bytes[] calldata _readyToDepositPubkey, + bytes[] calldata _frontRunPubkey, + bytes[] calldata _invalidSignaturePubkey + ) external; + + // return validator struct for a validator Id + function validatorRegistry(uint256) + external + view + returns ( + ValidatorStatus status, + bytes calldata pubkey, + bytes calldata preDepositSignature, + bytes calldata depositSignature, + address withdrawVaultAddress, + uint256 operatorId, + uint256 depositTime, + uint256 withdrawnTime + ); + + // returns the operator struct given operator Id + function operatorStructById(uint64) + external + view + returns ( + bool operatorType, + string calldata operatorName, + address payable operatorRewardAddress, + address operatorAddress, + uint64 operatorSSVID, + uint64 keyShareCount, + uint256 bondAmount + ); + + function onlyPreDepositValidator(bytes calldata _pubkey) external view; + + function increaseTotalActiveValidatorCount(uint256 _count) external; + + function nextQueuedValidatorIndex() external view returns (uint256); + + function updateNextQueuedValidatorIndex(uint256 _nextQueuedValidatorIndex) external; + + function updateInputKeyCountLimit(uint16 _inputKeyCountLimit) external; + + function updateBatchSizeToRemoveValidatorFromSSV(uint64 _batchSizeToRemoveValidatorFromSSV) external; + + function updateBatchSizeToRegisterValidatorFromSSV(uint64 _batchSizeToRegisterValidatorFromSSV) external; + + function markValidatorStatusAsPreDeposit(bytes calldata _pubkey) external; + + function getAllActiveValidators(uint256 _pageNumber, uint256 _pageSize) external view returns (Validator[] memory); + + // returns the total number of queued validators across all operators + function getTotalQueuedValidatorCount() external view returns (uint256); + + // returns the total number of active validators across all operators + function getTotalActiveValidatorCount() external view returns (uint256); + + function getCollateralETH() external view returns (uint256); + + function getOperatorTotalKeys(uint256 _operatorId) external view returns (uint256 _totalKeys); + + function operatorIDByAddress(address) external view returns (uint64); + + // function getOperatorRewardAddress(uint256 _operatorId) external view returns (address payable); + + function isExistingPubkey(bytes calldata _pubkey) external view returns (bool); + + function isExistingOperator(address _operAddr) external view returns (bool); + + function POOL_ID() external view returns (uint8); + + function CLUSTER_SIZE() external view returns (uint8); + + function inputKeyCountLimit() external view returns (uint16); + + function nextOperatorId() external view returns (uint64); + + function nextValidatorId() external view returns (uint256); + + function verifiedKeyBatchSize() external view returns (uint256); + + function totalActiveValidatorCount() external view returns (uint256); + + function validatorIdByPubkey(bytes calldata _pubkey) external view returns (uint256); + + function getOperatorsIdsForValidatorId(uint256 validatorId) external view returns (uint64[] memory); +} diff --git a/contracts/interfaces/DVT/SSV/ISSVPool.sol b/contracts/interfaces/DVT/SSV/ISSVPool.sol new file mode 100644 index 00000000..dd7fe7f4 --- /dev/null +++ b/contracts/interfaces/DVT/SSV/ISSVPool.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +interface ISSVPool { + // Errors + error UnsupportedOperation(); + error InvalidCommission(); + error CouldNotDetermineExcessETH(); + + // Events + event ValidatorPreDepositedOnBeaconChain(bytes pubKey); + event ValidatorDepositedOnBeaconChain(uint256 indexed validatorId, bytes pubKey); + event UpdatedCommissionFees(uint256 protocolFee, uint256 operatorFee); + event ReceivedCollateralETH(uint256 amount); + event UpdatedStaderConfig(address staderConfig); + event ReceivedInsuranceFund(uint256 amount); + event TransferredETHToSSPMForDefectiveKeys(uint256 amount); + + // Setters + + function setCommissionFees(uint256 _protocolFee, uint256 _operatorFee) external; // sets the commission fees, protocol and operator + + function receiveInsuranceFund() external payable; + + function transferETHOfDefectiveKeysToSSPM(uint256 _defectiveKeyCount) external; + + function fullDepositOnBeaconChain(bytes[] calldata _pubkey) external; + + //Getters + + function protocolFee() external view returns (uint256); // returns the protocol fee + + function operatorFee() external view returns (uint256); // returns the operator fee + + function stakeUserETHToBeaconChain() external payable; + + function getSocializingPoolAddress() external view returns (address); + + function getNodeRegistry() external view returns (address); +} diff --git a/contracts/interfaces/DVT/SSV/ISSVValidatorWithdrawalVault.sol b/contracts/interfaces/DVT/SSV/ISSVValidatorWithdrawalVault.sol new file mode 100644 index 00000000..cd6cc30e --- /dev/null +++ b/contracts/interfaces/DVT/SSV/ISSVValidatorWithdrawalVault.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import '../../IStaderConfig.sol'; + +interface ISSVValidatorWithdrawalVault { + // Errors + error InvalidRewardAmount(); + error NotEnoughRewardToDistribute(); + error CallerNotNodeRegistryContract(); + + // Events + event ETHReceived(address indexed sender, uint256 amount); + event DistributeRewardFailed(uint256 rewardAmount, uint256 rewardThreshold); + event DistributedRewards(uint256 userShare, uint256 operatorShare, uint256 protocolShare); + event SettledFunds(uint256 userShare, uint256 operatorShare, uint256 protocolShare); + event UpdatedStaderConfig(address _staderConfig); + + // methods + function distributeRewards() external; + + function settleFunds() external; + + // getters + + function calculateValidatorWithdrawalShare(uint8 _poolId, IStaderConfig _staderConfig) + external + view + returns ( + uint256 _userShare, + uint256 _operatorShare, + uint256 _protocolShare + ); +} diff --git a/contracts/interfaces/DVT/SSV/ISSVVaultProxy.sol b/contracts/interfaces/DVT/SSV/ISSVVaultProxy.sol new file mode 100644 index 00000000..822aa1b2 --- /dev/null +++ b/contracts/interfaces/DVT/SSV/ISSVVaultProxy.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import '../../IStaderConfig.sol'; + +interface ISSVVaultProxy { + error CallerNotOwner(); + error AlreadyInitialized(); + event UpdatedOwner(address owner); + event UpdatedStaderConfig(address staderConfig); + + //Getters + function vaultSettleStatus() external view returns (bool); + + function isInitialized() external view returns (bool); + + function poolId() external view returns (uint8); + + function validatorId() external view returns (uint256); + + function owner() external view returns (address); + + function staderConfig() external view returns (IStaderConfig); + + //Setters + function updateOwner() external; + + function updateStaderConfig(address _staderConfig) external; +} diff --git a/contracts/interfaces/IStaderConfig.sol b/contracts/interfaces/IStaderConfig.sol index 280f0cc9..25b55653 100644 --- a/contracts/interfaces/IStaderConfig.sol +++ b/contracts/interfaces/IStaderConfig.sol @@ -58,6 +58,14 @@ interface IStaderConfig { function VALIDATOR_WITHDRAWAL_VAULT_IMPLEMENTATION() external view returns (bytes32); + function SSV_POOL() external view returns (bytes32); + + function SSV_NODE_REGISTRY() external view returns (bytes32); + + function SSV_SOCIALIZING_POOL() external view returns (bytes32); + + function SSV_VALIDATOR_WITHDRAWAL_VAULT() external view returns (bytes32); + //POR Feed Proxy function ETH_BALANCE_POR_FEED() external view returns (bytes32); @@ -150,6 +158,14 @@ interface IStaderConfig { function getETHXSupplyPORFeedProxy() external view returns (address); + function getSSVPool() external view returns (address); + + function getSSVNodeRegistry() external view returns (address); + + function getSSVSocializingPool() external view returns (address); + + function getSSVValidatorWithdrawalVault() external view returns (address); + // Tokens function getStaderToken() external view returns (address); diff --git a/contracts/interfaces/IStaderInsuranceFund.sol b/contracts/interfaces/IStaderInsuranceFund.sol index ba95dd0c..53439a8d 100644 --- a/contracts/interfaces/IStaderInsuranceFund.sol +++ b/contracts/interfaces/IStaderInsuranceFund.sol @@ -6,6 +6,7 @@ interface IStaderInsuranceFund { error InvalidAmountProvided(); error TransferFailed(); error InSufficientBalance(); + error InvalidPoolToReimburse(); //Events event ReceivedInsuranceFund(uint256 amount); diff --git a/contracts/interfaces/IVaultFactory.sol b/contracts/interfaces/IVaultFactory.sol index 737c24ae..cf680db4 100644 --- a/contracts/interfaces/IVaultFactory.sol +++ b/contracts/interfaces/IVaultFactory.sol @@ -3,9 +3,11 @@ pragma solidity 0.8.16; interface IVaultFactory { event WithdrawVaultCreated(address withdrawVault); + event SSVValidatorWithdrawalVaultCreated(uint256 validatorId, address withdrawVaultAddress); event NodeELRewardVaultCreated(address nodeDistributor); event UpdatedStaderConfig(address staderConfig); event UpdatedVaultProxyImplementation(address vaultProxyImplementation); + event UpdatedSSVVaultProxyImplementation(address ssvVaultProxyImplementation); function NODE_REGISTRY_CONTRACT() external view returns (bytes32); @@ -16,6 +18,8 @@ interface IVaultFactory { uint256 _validatorId ) external returns (address); + function deploySSVValidatorWithdrawalVault(uint8 _poolId, uint256 _validatorId) external returns (address); + function deployNodeELRewardVault(uint8 _poolId, uint256 _operatorId) external returns (address); function computeWithdrawVaultAddress( @@ -24,6 +28,11 @@ interface IVaultFactory { uint256 _validatorCount ) external view returns (address); + function computeSSVValidatorWithdrawalVaultAddress(uint8 _poolId, uint256 _validatorId) + external + view + returns (address); + function computeNodeELRewardVaultAddress(uint8 _poolId, uint256 _operatorId) external view returns (address); function getValidatorWithdrawCredential(address _withdrawVault) external pure returns (bytes memory); @@ -31,4 +40,6 @@ interface IVaultFactory { function updateStaderConfig(address _staderConfig) external; function updateVaultProxyAddress(address _vaultProxyImpl) external; + + function updateSSVVaultProxyImplementation(address _ssvVaultProxyImpl) external; } diff --git a/contracts/interfaces/SDCollateral/ISDCollateral.sol b/contracts/interfaces/SDCollateral/ISDCollateral.sol index 36c72a69..e8222480 100644 --- a/contracts/interfaces/SDCollateral/ISDCollateral.sol +++ b/contracts/interfaces/SDCollateral/ISDCollateral.sol @@ -17,6 +17,7 @@ interface ISDCollateral { error InvalidPoolLimit(); error SDTransferFailed(); error NoStateChange(); + error CallerNotWithdrawVault(); // events event UpdatedStaderConfig(address indexed staderConfig); @@ -33,6 +34,12 @@ interface ISDCollateral { function slashValidatorSD(uint256 _validatorId, uint8 _poolId) external; + function slashSSVOperatorSD( + uint8 _poolId, + uint256 _validatorId, + uint64[] memory operatorIds + ) external; + function maxApproveSD() external; // setters diff --git a/contracts/interfaces/SSVNetwork/ISSVNetwork.sol b/contracts/interfaces/SSVNetwork/ISSVNetwork.sol new file mode 100644 index 00000000..fdd05f2a --- /dev/null +++ b/contracts/interfaces/SSVNetwork/ISSVNetwork.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import './ISSVNetworkCore.sol'; + +interface ISSVNetwork { + error ApprovalNotWithinTimeframe(); + error CallerNotOwner(); + error CallerNotWhitelisted(); + error ClusterAlreadyEnabled(); + error ClusterDoesNotExists(); + error ClusterIsLiquidated(); + error ClusterNotLiquidatable(); + error ExceedValidatorLimit(); + error FeeExceedsIncreaseLimit(); + error FeeIncreaseNotAllowed(); + error FeeTooLow(); + error IncorrectClusterState(); + error IncorrectValidatorState(); + error InsufficientBalance(); + error InvalidOperatorIdsLength(); + error InvalidPublicKeyLength(); + error MaxValueExceeded(); + error NewBlockPeriodIsBelowMinimum(); + error NoFeeDeclared(); + error NotAuthorized(); + error OperatorAlreadyExists(); + error OperatorDoesNotExist(); + error OperatorsListNotUnique(); + error SameFeeChangeNotAllowed(); + error TargetModuleDoesNotExist(); + error TokenTransferFailed(); + error UnsortedOperatorsList(); + error ValidatorAlreadyExists(); + error ValidatorDoesNotExist(); + event AdminChanged(address previousAdmin, address newAdmin); + event BeaconUpgraded(address indexed beacon); + event ClusterDeposited(address indexed owner, uint64[] operatorIds, uint256 value, ISSVNetworkCore.Cluster cluster); + event ClusterLiquidated(address indexed owner, uint64[] operatorIds, ISSVNetworkCore.Cluster cluster); + event ClusterReactivated(address indexed owner, uint64[] operatorIds, ISSVNetworkCore.Cluster cluster); + event ClusterWithdrawn(address indexed owner, uint64[] operatorIds, uint256 value, ISSVNetworkCore.Cluster cluster); + event DeclareOperatorFeePeriodUpdated(uint64 value); + event ExecuteOperatorFeePeriodUpdated(uint64 value); + event FeeRecipientAddressUpdated(address indexed owner, address recipientAddress); + event Initialized(uint8 version); + event LiquidationThresholdPeriodUpdated(uint64 value); + event MinimumLiquidationCollateralUpdated(uint256 value); + event NetworkEarningsWithdrawn(uint256 value, address recipient); + event NetworkFeeUpdated(uint256 oldFee, uint256 newFee); + event OperatorAdded(uint64 indexed operatorId, address indexed owner, bytes publicKey, uint256 fee); + event OperatorFeeCancellationDeclared(address indexed owner, uint64 indexed operatorId); + event OperatorFeeDeclared(address indexed owner, uint64 indexed operatorId, uint256 blockNumber, uint256 fee); + event OperatorFeeExecuted(address indexed owner, uint64 indexed operatorId, uint256 blockNumber, uint256 fee); + event OperatorFeeIncreaseLimitUpdated(uint64 value); + event OperatorRemoved(uint64 indexed operatorId); + event OperatorWhitelistUpdated(uint64 indexed operatorId, address whitelisted); + event OperatorWithdrawn(address indexed owner, uint64 indexed operatorId, uint256 value); + event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + event Upgraded(address indexed implementation); + event ValidatorAdded( + address indexed owner, + uint64[] operatorIds, + bytes publicKey, + bytes shares, + ISSVNetworkCore.Cluster cluster + ); + event ValidatorRemoved( + address indexed owner, + uint64[] operatorIds, + bytes publicKey, + ISSVNetworkCore.Cluster cluster + ); + + fallback() external; + + function acceptOwnership() external; + + function cancelDeclaredOperatorFee(uint64 operatorId) external; + + function declareOperatorFee(uint64 operatorId, uint256 fee) external; + + function deposit( + address owner, + uint64[] memory operatorIds, + uint256 amount, + ISSVNetworkCore.Cluster memory cluster + ) external; + + function executeOperatorFee(uint64 operatorId) external; + + function getRegisterAuth(address userAddress) external view returns (bool authOperators, bool authValidators); + + function initialize( + address token_, + address ssvOperators_, + address ssvClusters_, + address ssvDAO_, + address ssvViews_, + uint64 minimumBlocksBeforeLiquidation_, + uint256 minimumLiquidationCollateral_, + uint32 validatorsPerOperatorLimit_, + uint64 declareOperatorFeePeriod_, + uint64 executeOperatorFeePeriod_, + uint64 operatorMaxFeeIncrease_ + ) external; + + function liquidate( + address owner, + uint64[] memory operatorIds, + ISSVNetworkCore.Cluster memory cluster + ) external; + + function owner() external view returns (address); + + function pendingOwner() external view returns (address); + + function proxiableUUID() external view returns (bytes32); + + function reactivate( + uint64[] memory operatorIds, + uint256 amount, + ISSVNetworkCore.Cluster memory cluster + ) external; + + function reduceOperatorFee(uint64 operatorId, uint256 fee) external; + + function registerOperator(bytes memory publicKey, uint256 fee) external returns (uint64 id); + + function registerValidator( + bytes memory publicKey, + uint64[] memory operatorIds, + bytes memory sharesData, + uint256 amount, + ISSVNetworkCore.Cluster memory cluster + ) external; + + function removeOperator(uint64 operatorId) external; + + function removeValidator( + bytes memory publicKey, + uint64[] memory operatorIds, + ISSVNetworkCore.Cluster memory cluster + ) external; + + function renounceOwnership() external; + + function setFeeRecipientAddress(address recipientAddress) external; + + function setOperatorWhitelist(uint64 operatorId, address whitelisted) external; + + function setRegisterAuth( + address userAddress, + bool authOperator, + bool authValidator + ) external; + + function transferOwnership(address newOwner) external; + + function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external; + + function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external; + + function updateLiquidationThresholdPeriod(uint64 blocks) external; + + function updateMinimumLiquidationCollateral(uint256 amount) external; + + function updateModule(uint8 moduleId, address moduleAddress) external; + + function updateNetworkFee(uint256 fee) external; + + function updateOperatorFeeIncreaseLimit(uint64 percentage) external; + + function upgradeTo(address newImplementation) external; + + function upgradeToAndCall(address newImplementation, bytes memory data) external payable; + + function withdraw( + uint64[] memory operatorIds, + uint256 amount, + ISSVNetworkCore.Cluster memory cluster + ) external; + + function withdrawNetworkEarnings(uint256 amount) external; + + function withdrawOperatorEarnings(uint64 operatorId, uint256 amount) external; + + function withdrawOperatorEarnings(uint64 operatorId) external; +} diff --git a/contracts/interfaces/SSVNetwork/ISSVNetworkCore.sol b/contracts/interfaces/SSVNetwork/ISSVNetworkCore.sol new file mode 100644 index 00000000..0a3d963b --- /dev/null +++ b/contracts/interfaces/SSVNetwork/ISSVNetworkCore.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +interface ISSVNetworkCore { + struct Cluster { + uint32 validatorCount; + uint64 networkFeeIndex; + uint64 index; + bool active; + uint256 balance; + } +} diff --git a/contracts/interfaces/SSVNetwork/ISSVNetworkViews.sol b/contracts/interfaces/SSVNetwork/ISSVNetworkViews.sol new file mode 100644 index 00000000..9550cb5b --- /dev/null +++ b/contracts/interfaces/SSVNetwork/ISSVNetworkViews.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.16; + +import './ISSVNetworkCore.sol'; + +interface ISSVNetworkViews { + error ApprovalNotWithinTimeframe(); + error CallerNotOwner(); + error CallerNotWhitelisted(); + error ClusterAlreadyEnabled(); + error ClusterDoesNotExists(); + error ClusterIsLiquidated(); + error ClusterNotLiquidatable(); + error ExceedValidatorLimit(); + error FeeExceedsIncreaseLimit(); + error FeeIncreaseNotAllowed(); + error FeeTooLow(); + error IncorrectClusterState(); + error IncorrectValidatorState(); + error InsufficientBalance(); + error InvalidOperatorIdsLength(); + error InvalidPublicKeyLength(); + error MaxValueExceeded(); + error NewBlockPeriodIsBelowMinimum(); + error NoFeeDeclared(); + error NotAuthorized(); + error OperatorAlreadyExists(); + error OperatorDoesNotExist(); + error OperatorsListNotUnique(); + error SameFeeChangeNotAllowed(); + error TargetModuleDoesNotExist(); + error TokenTransferFailed(); + error UnsortedOperatorsList(); + error ValidatorAlreadyExists(); + error ValidatorDoesNotExist(); + event AdminChanged(address previousAdmin, address newAdmin); + event BeaconUpgraded(address indexed beacon); + event Initialized(uint8 version); + event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + event Upgraded(address indexed implementation); + + function acceptOwnership() external; + + function getBalance( + address owner, + uint64[] memory operatorIds, + ISSVNetworkCore.Cluster memory cluster + ) external view returns (uint256); + + function getBurnRate( + address owner, + uint64[] memory operatorIds, + ISSVNetworkCore.Cluster memory cluster + ) external view returns (uint256); + + function getLiquidationThresholdPeriod() external view returns (uint64); + + function getMinimumLiquidationCollateral() external view returns (uint256); + + function getNetworkEarnings() external view returns (uint256); + + function getNetworkFee() external view returns (uint256); + + function getOperatorById(uint64 operatorId) + external + view + returns ( + address, + uint256, + uint32, + address, + bool, + bool + ); + + function getOperatorDeclaredFee(uint64 operatorId) + external + view + returns ( + uint256, + uint64, + uint64 + ); + + function getOperatorEarnings(uint64 id) external view returns (uint256); + + function getOperatorFee(uint64 operatorId) external view returns (uint256); + + function getOperatorFeeIncreaseLimit() external view returns (uint64 operatorMaxFeeIncrease); + + function getOperatorFeePeriods() + external + view + returns (uint64 declareOperatorFeePeriod, uint64 executeOperatorFeePeriod); + + function getValidator(address owner, bytes memory publicKey) external view returns (bool active); + + function getValidatorsPerOperatorLimit() external view returns (uint32); + + function getVersion() external view returns (string memory version); + + function initialize(address ssvNetwork_) external; + + function isLiquidatable( + address owner, + uint64[] memory operatorIds, + ISSVNetworkCore.Cluster memory cluster + ) external view returns (bool); + + function isLiquidated( + address owner, + uint64[] memory operatorIds, + ISSVNetworkCore.Cluster memory cluster + ) external view returns (bool); + + function owner() external view returns (address); + + function pendingOwner() external view returns (address); + + function proxiableUUID() external view returns (bytes32); + + function renounceOwnership() external; + + function ssvNetwork() external view returns (address); + + function transferOwnership(address newOwner) external; + + function upgradeTo(address newImplementation) external; + + function upgradeToAndCall(address newImplementation, bytes memory data) external payable; +} diff --git a/hardhat.config.ts b/hardhat.config.ts index c1783d3d..b669c1c3 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -23,6 +23,15 @@ const config: HardhatUserConfig = { }, }, }, + { + version: '0.8.18', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + } ], }, defaultNetwork: 'mainnet', From 8c16287913e669a6ff3d6170dd65960b1afe466d Mon Sep 17 00:00:00 2001 From: Sanjay Yadav Date: Tue, 12 Sep 2023 08:26:22 +0530 Subject: [PATCH 2/4] pull cluster size from node registry in SSV Withdrawal Vault --- .../DVT/SSV/SSVValidatorWithdrawalVault.sol | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol b/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol index b1cf5113..8744601f 100644 --- a/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol +++ b/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol @@ -44,8 +44,6 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { (uint256 userShare, uint256 operatorShare, uint256 protocolShare) = IPoolUtils(staderConfig.getPoolUtils()) .calculateRewardShare(poolId, totalRewards); - //TODO how to make sure only 4 operators - //TODO pull this number 4 from somewhere maybe ssv node registry //TODO add documentation for this formula // Distribute rewards uint64[] memory operatorIds = nodeRegistry.getOperatorsIdsForValidatorId(validatorId); @@ -57,15 +55,15 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { operatorKeyLevelShare = getPermissionedOperatorShare( operatorShare, totalRewards, - nodeRegistry.getCollateralETH(), - staderConfig.getStakedEthPerNode() + staderConfig.getStakedEthPerNode(), + nodeRegistry ); } else { operatorKeyLevelShare = getPermissionlessOperatorShare( operatorShare, totalRewards, - nodeRegistry.getCollateralETH(), - staderConfig.getStakedEthPerNode() + staderConfig.getStakedEthPerNode(), + nodeRegistry ); } IOperatorRewardsCollector(staderConfig.getOperatorRewardsCollector()).depositFor{ @@ -100,24 +98,24 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { uint256 permissionlessOperatorShare; uint256 collateralETH = ISSVNodeRegistry(msg.sender).getCollateralETH(); if (operatorShare <= collateralETH) { - permissionlessOperatorShare = operatorShare / 2; + permissionlessOperatorShare = operatorShare / (ISSVNodeRegistry(msg.sender).CLUSTER_SIZE() / 2); } else { uint256 rewards = address(this).balance - staderConfig.getStakedEthPerNode(); permissionedOperatorShare = getPermissionedOperatorShare( operatorShare - collateralETH, rewards, - collateralETH, - staderConfig.getStakedEthPerNode() + staderConfig.getStakedEthPerNode(), + ISSVNodeRegistry(msg.sender) ); permissionlessOperatorShare = collateralETH / - 2 + + (ISSVNodeRegistry(msg.sender).CLUSTER_SIZE() / 2) + getPermissionlessOperatorShare( operatorShare - collateralETH, rewards, - collateralETH, - staderConfig.getStakedEthPerNode() + staderConfig.getStakedEthPerNode(), + ISSVNodeRegistry(msg.sender) ); } @@ -128,12 +126,14 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { } uint256 userShare = userSharePrelim + penaltyAmount; - if (penaltyAmount >= 4 * permissionedOperatorShare) { - permissionlessOperatorShare -= (penaltyAmount - 2 * permissionedOperatorShare) / 2; + if (penaltyAmount >= ISSVNodeRegistry(msg.sender).CLUSTER_SIZE() * permissionedOperatorShare) { + permissionlessOperatorShare -= + (penaltyAmount - (ISSVNodeRegistry(msg.sender).CLUSTER_SIZE() / 2) * permissionedOperatorShare) / + 2; permissionedOperatorShare = 0; } else { - permissionedOperatorShare -= penaltyAmount / 4; - permissionlessOperatorShare -= penaltyAmount / 4; + permissionedOperatorShare -= penaltyAmount / ISSVNodeRegistry(msg.sender).CLUSTER_SIZE(); + permissionlessOperatorShare -= penaltyAmount / ISSVNodeRegistry(msg.sender).CLUSTER_SIZE(); } // Final settlement @@ -209,18 +209,20 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { function getPermissionedOperatorShare( uint256 operatorShare, uint256 rewards, - uint256 collateralETH, - uint256 ETH_PER_NODE - ) internal pure returns (uint256) { - return (operatorShare - (rewards * collateralETH) / ETH_PER_NODE) / 4; + uint256 ETH_PER_NODE, + ISSVNodeRegistry nodeRegistry + ) internal view returns (uint256) { + return + (operatorShare - (rewards * nodeRegistry.getCollateralETH()) / ETH_PER_NODE) / nodeRegistry.CLUSTER_SIZE(); } function getPermissionlessOperatorShare( uint256 operatorShare, uint256 rewards, - uint256 collateralETH, - uint256 ETH_PER_NODE - ) internal pure returns (uint256) { - return (operatorShare + (rewards * collateralETH) / ETH_PER_NODE) / 4; + uint256 ETH_PER_NODE, + ISSVNodeRegistry nodeRegistry + ) internal view returns (uint256) { + return + (operatorShare + (rewards * nodeRegistry.getCollateralETH()) / ETH_PER_NODE) / nodeRegistry.CLUSTER_SIZE(); } } From 1e7e74a1f5b12bbd1a53aa227ef0a12b5c02243b Mon Sep 17 00:00:00 2001 From: Sanjay Yadav Date: Thu, 14 Sep 2023 09:34:34 +0530 Subject: [PATCH 3/4] getOperatorRewardAddress in UtilLib fix & check validator registration on SSV while doing full deposit --- contracts/DVT/SSV/SSVNodeRegistry.sol | 56 +++++++++++++------ contracts/DVT/SSV/SSVPool.sol | 2 +- .../DVT/SSV/SSVValidatorWithdrawalVault.sol | 4 +- contracts/SDCollateral.sol | 2 +- .../interfaces/DVT/SSV/ISSVNodeRegistry.sol | 17 +++--- .../interfaces/SDCollateral/ISDCollateral.sol | 2 +- 6 files changed, 54 insertions(+), 29 deletions(-) diff --git a/contracts/DVT/SSV/SSVNodeRegistry.sol b/contracts/DVT/SSV/SSVNodeRegistry.sol index d13ee525..1d2bc879 100644 --- a/contracts/DVT/SSV/SSVNodeRegistry.sol +++ b/contracts/DVT/SSV/SSVNodeRegistry.sol @@ -38,7 +38,7 @@ contract SSVNodeRegistry is ISSVNetwork public ssvNetwork; ISSVNetworkViews public ssvNetworkViews; - uint64 public nextOperatorId; + uint256 public nextOperatorId; uint256 public nextValidatorId; uint256 public verifiedKeyBatchSize; uint256 public nextQueuedValidatorIndex; @@ -50,12 +50,12 @@ contract SSVNodeRegistry is mapping(uint256 => Validator) public validatorRegistry; // mapping of validator public key and validator Id mapping(bytes => uint256) public validatorIdByPubkey; - //mapping of SSV operators assigned to a validator - mapping(bytes => uint64[]) public operatorIdssByPubkey; + //mapping of stader operators Ids assigned to a validator + mapping(bytes => uint256[]) public operatorIdssByPubkey; // mapping of operator Id and Operator struct - mapping(uint64 => SSVOperator) public operatorStructById; + mapping(uint256 => SSVOperator) public operatorStructById; // mapping of operator address and operator Id - mapping(address => uint64) public operatorIDByAddress; + mapping(address => uint256) public operatorIDByAddress; // mapping of whitelisted permissioned node operator mapping(address => bool) public permissionList; @@ -153,7 +153,7 @@ contract SSVNodeRegistry is * amount of collateral should be multiple of collateral required per key-share */ function depositCollateral() external payable { - uint64 operatorId = operatorIDByAddress[msg.sender]; + uint256 operatorId = operatorIDByAddress[msg.sender]; _verifyOperatorAndDepositAmount(operatorId, msg.value); operatorStructById[operatorId].bondAmount += msg.value; emit BondDeposited(msg.sender, msg.value); @@ -271,7 +271,7 @@ contract SSVNodeRegistry is */ function registerValidatorsWithSSV( bytes[] calldata publicKey, - uint64[][] memory staderOperatorIds, + uint256[][] memory staderOperatorIds, bytes[] calldata sharesData, ISSVNetworkCore.Cluster[] memory cluster ) external { @@ -333,7 +333,11 @@ contract SSVNodeRegistry is if (validatorRegistry[validatorId].status != ValidatorStatus.WITHDRAWN) { revert ValidatorNotWithdrawn(); } - ssvNetwork.removeValidator(publicKey[i], operatorIdssByPubkey[publicKey[i]], cluster[i]); + ssvNetwork.removeValidator( + publicKey[i], + _getSSVOperatorIds(operatorIdssByPubkey[publicKey[i]]), + cluster[i] + ); } } @@ -430,8 +434,12 @@ contract SSVNodeRegistry is emit UpdatedStaderConfig(_staderConfig); } - // check for only PRE_DEPOSIT state validators - function onlyPreDepositValidator(bytes calldata _pubkey) external view { + // check for the validator being registered with SSV along with the PRE_DEPOSIT status + function onlySSVRegisteredAndPreDepositValidator(bytes calldata _pubkey) external view { + bool active = ssvNetworkViews.getValidator(address(this), _pubkey); + if (!active) { + revert ValidatorNotRegisteredWithSSV(); + } uint256 validatorId = validatorIdByPubkey[_pubkey]; _onlyPreDepositValidator(validatorId); } @@ -472,7 +480,15 @@ contract SSVNodeRegistry is * @param _operatorId Id of node operator */ function getOperatorTotalKeys(uint256 _operatorId) public view override returns (uint256 _totalKeys) { - _totalKeys = operatorStructById[uint64(_operatorId)].keyShareCount; + _totalKeys = operatorStructById[_operatorId].keyShareCount; + } + + /** + * @notice returns the operator reward address + * @param _operatorId operator Id + */ + function getOperatorRewardAddress(uint256 _operatorId) external view override returns (address payable) { + return operatorStructById[_operatorId].operatorRewardAddress; } /** @@ -488,11 +504,12 @@ contract SSVNodeRegistry is uint256 _startIndex, uint256 _endIndex ) external view returns (uint64) { - uint64 operatorId = operatorIDByAddress[_nodeOperator]; + uint256 operatorId = operatorIDByAddress[_nodeOperator]; return operatorStructById[operatorId].keyShareCount; } - function getOperatorsIdsForValidatorId(uint256 validatorId) external view returns (uint64[] memory) { + // returns stader defined operator IDs of SSV operators running a validator + function getOperatorsIdsForValidatorId(uint256 validatorId) external view returns (uint256[] memory) { return operatorIdssByPubkey[validatorRegistry[validatorId].pubkey]; } @@ -534,7 +551,7 @@ contract SSVNodeRegistry is return (validator.status == ValidatorStatus.PRE_DEPOSIT || validator.status == ValidatorStatus.DEPOSITED); } - function _verifyOperatorAndDepositAmount(uint64 _operatorId, uint256 _depositAmount) internal view { + function _verifyOperatorAndDepositAmount(uint256 _operatorId, uint256 _depositAmount) internal view { if (_operatorId == 0 || operatorStructById[_operatorId].operatorType) { revert OperatorNotOnboardOrPermissioned(); } @@ -556,7 +573,7 @@ contract SSVNodeRegistry is } } - function _validateStaderOperators(uint64[] memory staderOperatorIds) + function _validateStaderOperators(uint256[] memory staderOperatorIds) internal returns (uint64[] memory ssvOperatorIds) { @@ -587,6 +604,13 @@ contract SSVNodeRegistry is } } + function _getSSVOperatorIds(uint256[] memory staderOperatorIds) internal returns (uint64[] memory ssvOperatorIds) { + ssvOperatorIds = new uint64[](CLUSTER_SIZE); + for (uint64 i = 0; i < CLUSTER_SIZE; i++) { + ssvOperatorIds[i] = operatorStructById[staderOperatorIds[i]].operatorSSVID; + } + } + function _onlyPreDepositValidator(uint256 _validatorId) internal view { if (validatorRegistry[_validatorId].status != ValidatorStatus.PRE_DEPOSIT) { revert UNEXPECTED_STATUS(); @@ -600,7 +624,7 @@ contract SSVNodeRegistry is } function _decreaseOperatorsKeyShareCount(bytes calldata pubkey) internal { - uint64[] memory operatorIds = operatorIdssByPubkey[pubkey]; + uint256[] memory operatorIds = operatorIdssByPubkey[pubkey]; for (uint256 j; j < operatorIds.length; ) { operatorStructById[operatorIds[j]].keyShareCount -= 1; unchecked { diff --git a/contracts/DVT/SSV/SSVPool.sol b/contracts/DVT/SSV/SSVPool.sol index b4ad7bd6..eb83506c 100644 --- a/contracts/DVT/SSV/SSVPool.sol +++ b/contracts/DVT/SSV/SSVPool.sol @@ -109,7 +109,7 @@ contract SSVPool is ISSVPool, AccessControlUpgradeable, ReentrancyGuardUpgradeab //decrease the preDeposit validator count _decreasePreDepositValidatorCount(pubkeyCount); for (uint256 i; i < pubkeyCount; ) { - ISSVNodeRegistry(nodeRegistryAddress).onlyPreDepositValidator(_pubkey[i]); + ISSVNodeRegistry(nodeRegistryAddress).onlySSVRegisteredAndPreDepositValidator(_pubkey[i]); uint256 validatorId = INodeRegistry(nodeRegistryAddress).validatorIdByPubkey(_pubkey[i]); (, , , bytes memory depositSignature, address withdrawVaultAddress, , , ) = INodeRegistry( nodeRegistryAddress diff --git a/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol b/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol index 8744601f..0e0d2ce9 100644 --- a/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol +++ b/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol @@ -46,7 +46,7 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { //TODO add documentation for this formula // Distribute rewards - uint64[] memory operatorIds = nodeRegistry.getOperatorsIdsForValidatorId(validatorId); + uint256[] memory operatorIds = nodeRegistry.getOperatorsIdsForValidatorId(validatorId); uint256 totalOperators = operatorIds.length; for (uint8 i; i < totalOperators; ) { (bool operatorType, , , address operatorAddress, , , ) = nodeRegistry.operatorStructById(operatorIds[i]); @@ -85,7 +85,7 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { if (msg.sender != IPoolUtils(staderConfig.getPoolUtils()).getNodeRegistry(poolId)) { revert CallerNotNodeRegistryContract(); } - uint64[] memory operatorIds = ISSVNodeRegistry(msg.sender).getOperatorsIdsForValidatorId(validatorId); + uint256[] memory operatorIds = ISSVNodeRegistry(msg.sender).getOperatorsIdsForValidatorId(validatorId); uint256 totalOperators = operatorIds.length; (uint256 userSharePrelim, uint256 operatorShare, uint256 protocolShare) = calculateValidatorWithdrawalShare( diff --git a/contracts/SDCollateral.sol b/contracts/SDCollateral.sol index ff59c0cd..b3d8cec2 100644 --- a/contracts/SDCollateral.sol +++ b/contracts/SDCollateral.sol @@ -87,7 +87,7 @@ contract SDCollateral is ISDCollateral, AccessControlUpgradeable, ReentrancyGuar function slashSSVOperatorSD( uint8 _poolId, uint256 _validatorId, - uint64[] memory operatorIds + uint256[] memory operatorIds ) external override nonReentrant { (, , , , address withdrawVaultAddress, , , ) = INodeRegistry( IPoolUtils(staderConfig.getPoolUtils()).getNodeRegistry(_poolId) diff --git a/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol b/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol index be1ed201..eb18a87a 100644 --- a/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol +++ b/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol @@ -29,6 +29,7 @@ interface ISSVNodeRegistry { error TooManyWithdrawnKeysReported(); error CallerFailingSSVOperatorChecks(); error DuplicatePoolIDOrPoolNotAdded(); + error ValidatorNotRegisteredWithSSV(); error OperatorNotOnboardOrPermissioned(); error OperatorAlreadyOnBoardedInProtocol(); error NotSufficientCollateralPerKeyShare(); @@ -75,7 +76,7 @@ interface ISSVNodeRegistry { ); // returns the operator struct given operator Id - function operatorStructById(uint64) + function operatorStructById(uint256) external view returns ( @@ -88,7 +89,7 @@ interface ISSVNodeRegistry { uint256 bondAmount ); - function onlyPreDepositValidator(bytes calldata _pubkey) external view; + function onlySSVRegisteredAndPreDepositValidator(bytes calldata _pubkey) external view; function increaseTotalActiveValidatorCount(uint256 _count) external; @@ -106,19 +107,19 @@ interface ISSVNodeRegistry { function getAllActiveValidators(uint256 _pageNumber, uint256 _pageSize) external view returns (Validator[] memory); - // returns the total number of queued validators across all operators + // returns the total number of queued validators function getTotalQueuedValidatorCount() external view returns (uint256); - // returns the total number of active validators across all operators + // returns the total number of active validators function getTotalActiveValidatorCount() external view returns (uint256); function getCollateralETH() external view returns (uint256); function getOperatorTotalKeys(uint256 _operatorId) external view returns (uint256 _totalKeys); - function operatorIDByAddress(address) external view returns (uint64); + function operatorIDByAddress(address) external view returns (uint256); - // function getOperatorRewardAddress(uint256 _operatorId) external view returns (address payable); + function getOperatorRewardAddress(uint256 _operatorId) external view returns (address payable); function isExistingPubkey(bytes calldata _pubkey) external view returns (bool); @@ -130,7 +131,7 @@ interface ISSVNodeRegistry { function inputKeyCountLimit() external view returns (uint16); - function nextOperatorId() external view returns (uint64); + function nextOperatorId() external view returns (uint256); function nextValidatorId() external view returns (uint256); @@ -140,5 +141,5 @@ interface ISSVNodeRegistry { function validatorIdByPubkey(bytes calldata _pubkey) external view returns (uint256); - function getOperatorsIdsForValidatorId(uint256 validatorId) external view returns (uint64[] memory); + function getOperatorsIdsForValidatorId(uint256 validatorId) external view returns (uint256[] memory); } diff --git a/contracts/interfaces/SDCollateral/ISDCollateral.sol b/contracts/interfaces/SDCollateral/ISDCollateral.sol index e8222480..41a0fda6 100644 --- a/contracts/interfaces/SDCollateral/ISDCollateral.sol +++ b/contracts/interfaces/SDCollateral/ISDCollateral.sol @@ -37,7 +37,7 @@ interface ISDCollateral { function slashSSVOperatorSD( uint8 _poolId, uint256 _validatorId, - uint64[] memory operatorIds + uint256[] memory operatorIds ) external; function maxApproveSD() external; From 6a6ebef7beae7215ff3b6a0f60b2ede6023d9086 Mon Sep 17 00:00:00 2001 From: Sanjay Yadav Date: Tue, 26 Sep 2023 20:50:22 +0530 Subject: [PATCH 4/4] manual testing fixes and scripts --- contracts/DVT/SSV/SSVNodeRegistry.sol | 139 +- contracts/DVT/SSV/SSVPool.sol | 6 +- .../DVT/SSV/SSVValidatorWithdrawalVault.sol | 13 +- contracts/SDCollateral.sol | 14 +- contracts/StaderInsuranceFund.sol | 2 +- contracts/factory/VaultFactory.sol | 2 +- .../interfaces/DVT/SSV/ISSVNodeRegistry.sol | 58 +- contracts/interfaces/DVT/SSV/ISSVPool.sol | 3 +- package-lock.json | 1832 ++++++++++++++++- package.json | 4 +- scripts/KeyShareGeneration/clusterInfo.ts | 27 + scripts/KeyShareGeneration/keyShareSplit.ts | 62 + scripts/addValidatorKeys.ts | 12 +- scripts/customError.ts | 4 +- scripts/deployContractWithSSV.ts | 152 ++ scripts/distributeRewards.ts | 11 + scripts/registerValidatorWithSSV.ts | 53 + scripts/removeValidatorFromSSV.ts | 41 + scripts/submitOracleReport.ts | 27 +- scripts/upgrade/ssvNodeRegistry.ts | 15 + scripts/upgrade/ssvPool.ts | 15 + scripts/upgrade/staderInsuranceFund.ts | 2 +- scripts/verifyContracts.ts | 12 + 23 files changed, 2337 insertions(+), 169 deletions(-) create mode 100644 scripts/KeyShareGeneration/clusterInfo.ts create mode 100644 scripts/KeyShareGeneration/keyShareSplit.ts create mode 100644 scripts/deployContractWithSSV.ts create mode 100644 scripts/distributeRewards.ts create mode 100644 scripts/registerValidatorWithSSV.ts create mode 100644 scripts/removeValidatorFromSSV.ts create mode 100644 scripts/upgrade/ssvNodeRegistry.ts create mode 100644 scripts/upgrade/ssvPool.ts diff --git a/contracts/DVT/SSV/SSVNodeRegistry.sol b/contracts/DVT/SSV/SSVNodeRegistry.sol index 1d2bc879..6bf92bea 100644 --- a/contracts/DVT/SSV/SSVNodeRegistry.sol +++ b/contracts/DVT/SSV/SSVNodeRegistry.sol @@ -18,6 +18,7 @@ import '../../interfaces/SDCollateral/ISDCollateral.sol'; import '../../interfaces/SSVNetwork/ISSVNetworkViews.sol'; import '../../interfaces/DVT/SSV/ISSVValidatorWithdrawalVault.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol'; @@ -35,6 +36,7 @@ contract SSVNodeRegistry is uint64 public batchSizeToRegisterValidatorFromSSV; IStaderConfig public staderConfig; + address public ssvToken; ISSVNetwork public ssvNetwork; ISSVNetworkViews public ssvNetworkViews; @@ -58,6 +60,8 @@ contract SSVNodeRegistry is mapping(address => uint256) public operatorIDByAddress; // mapping of whitelisted permissioned node operator mapping(address => bool) public permissionList; + // mapping of proposed reward address corresponding to operator Id + mapping(uint256 => address) public proposedRewardAddressByOperatorId; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -67,25 +71,30 @@ contract SSVNodeRegistry is function initialize( address _admin, address _staderConfig, + address _ssvToken, address _ssvNetwork, address _ssvNetworkViews ) external initializer { UtilLib.checkNonZeroAddress(_admin); UtilLib.checkNonZeroAddress(_staderConfig); + UtilLib.checkNonZeroAddress(_ssvToken); UtilLib.checkNonZeroAddress(_ssvNetwork); UtilLib.checkNonZeroAddress(_ssvNetworkViews); __AccessControl_init_unchained(); __Pausable_init(); __ReentrancyGuard_init(); staderConfig = IStaderConfig(_staderConfig); + ssvToken = _ssvToken; ssvNetwork = ISSVNetwork(_ssvNetwork); ssvNetworkViews = ISSVNetworkViews(_ssvNetworkViews); nextOperatorId = 1; nextValidatorId = 1; inputKeyCountLimit = 30; verifiedKeyBatchSize = 50; + nextQueuedValidatorIndex = 1; batchSizeToRemoveValidatorFromSSV = 10; batchSizeToRegisterValidatorFromSSV = 10; + IERC20(ssvToken).approve(address(ssvNetwork), type(uint256).max); _grantRole(DEFAULT_ADMIN_ROLE, _admin); } @@ -136,12 +145,16 @@ contract SSVNodeRegistry is operatorStructById[nextOperatorId] = SSVOperator( permissionList[msg.sender], _operatorName, - _operatorRewardAddress, + payable(msg.sender), msg.sender, _ssvOperatorID, 0, 0 ); + if (_operatorRewardAddress != msg.sender) { + proposedRewardAddressByOperatorId[nextOperatorId] = _operatorRewardAddress; + emit InitiatedRewardAddressChange(msg.sender, _operatorRewardAddress); + } operatorIDByAddress[msg.sender] = nextOperatorId; nextOperatorId++; emit SSVOperatorOnboard(msg.sender, nextOperatorId - 1); @@ -259,7 +272,9 @@ contract SSVNodeRegistry is _decreaseTotalActiveValidatorCount(totalDefectedKeys); ISSVPool(ssvPool).transferETHOfDefectiveKeysToSSPM(totalDefectedKeys); } - ISSVPool(ssvPool).fullDepositOnBeaconChain(_readyToDepositPubkey); + ISSVPool(ssvPool).fullDepositOnBeaconChain{value: readyToDepositValidatorsLength * COLLATERAL_ETH}( + _readyToDepositPubkey + ); } /** @@ -270,6 +285,7 @@ contract SSVNodeRegistry is * @param cluster cluster array */ function registerValidatorsWithSSV( + uint256 _tokenAmount, bytes[] calldata publicKey, uint256[][] memory staderOperatorIds, bytes[] calldata sharesData, @@ -283,10 +299,11 @@ contract SSVNodeRegistry is if (keyCount != staderOperatorIds.length || keyCount != sharesData.length || keyCount != cluster.length) { revert MisMatchingInputKeysSize(); } + uint256 tokenPerValidator = _tokenAmount / keyCount; for (uint256 i; i < keyCount; ) { operatorIdssByPubkey[publicKey[i]] = staderOperatorIds[i]; uint64[] memory ssvOperatorIds = _validateStaderOperators(staderOperatorIds[i]); - ssvNetwork.registerValidator(publicKey[i], ssvOperatorIds, sharesData[i], 0, cluster[i]); + ssvNetwork.registerValidator(publicKey[i], ssvOperatorIds, sharesData[i], tokenPerValidator, cluster[i]); unchecked { ++i; } @@ -298,7 +315,7 @@ contract SSVNodeRegistry is * @dev list of pubkeys reported by oracle * @param _pubkeys array of withdrawn validators pubkey */ - function withdrawnValidators(bytes[] calldata _pubkeys) external { + function withdrawnValidators(bytes[] calldata _pubkeys) external override { UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.STADER_ORACLE()); uint256 withdrawnValidatorCount = _pubkeys.length; if (withdrawnValidatorCount > staderConfig.getWithdrawnKeyBatchSize()) { @@ -341,6 +358,18 @@ contract SSVNodeRegistry is } } + /** + * @notice sets the deposit block for a validator and update status to DEPOSITED + * @dev only ssv pool can call + * @param _validatorId Id of the validator + */ + function updateDepositStatusAndBlock(uint256 _validatorId) external override { + UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.SSV_POOL()); + validatorRegistry[_validatorId].depositBlock = block.number; + validatorRegistry[_validatorId].status = ValidatorStatus.DEPOSITED; + emit UpdatedValidatorDepositBlock(_validatorId, block.number); + } + /** * @notice update the status of a validator to `PRE_DEPOSIT` * @dev only `SSV_POOL` contract can call @@ -434,6 +463,87 @@ contract SSVNodeRegistry is emit UpdatedStaderConfig(_staderConfig); } + /** + * @notice propose the new reward address of an operator + * @dev only the existing reward address (msg.sender) can propose + * @param _operatorAddress operator address + * @param _rewardAddress new reward address + */ + function initiateRewardAddressChange(address _operatorAddress, address _rewardAddress) external override { + UtilLib.checkNonZeroAddress(_rewardAddress); + uint256 operatorId = operatorIDByAddress[_operatorAddress]; + address operatorRewardAddress = operatorStructById[operatorId].operatorRewardAddress; + if (msg.sender != operatorRewardAddress) { + revert CallerNotExistingRewardAddress(); + } + proposedRewardAddressByOperatorId[operatorId] = _rewardAddress; + emit InitiatedRewardAddressChange(_operatorAddress, _rewardAddress); + } + + /** + * @notice confirms and sets the new reward address of an operator + * @dev only the new reward address (msg.sender) can confirm + * @param _operatorAddress operator address + */ + function confirmRewardAddressChange(address _operatorAddress) external override { + uint256 _operatorId = operatorIDByAddress[_operatorAddress]; + if (msg.sender != proposedRewardAddressByOperatorId[_operatorId]) { + revert OnlyNewRewardAddressCanConfirm(); + } + delete proposedRewardAddressByOperatorId[_operatorId]; + + operatorStructById[_operatorId].operatorRewardAddress = payable(msg.sender); + emit UpdatedOperatorRewardAddress(_operatorAddress, msg.sender); + } + + /** + * @notice update the name of an operator + * @dev only operator msg.sender can update + * @param _operatorName new name of the operator + */ + function updateOperatorName(string calldata _operatorName) external override { + IPoolUtils(staderConfig.getPoolUtils()).onlyValidName(_operatorName); + uint256 operatorId = operatorIDByAddress[msg.sender]; + if (operatorId == 0) { + revert OperatorNotOnBoarded(); + } + operatorStructById[operatorId].operatorName = _operatorName; + emit UpdatedOperatorName(msg.sender, _operatorName); + } + + /** + * @notice update the address of SSV Token + * @dev only ADMIN role can update + * @param _ssvToken new address of SSV Token + */ + function updateSSVTokenAddress(address _ssvToken) external onlyRole(DEFAULT_ADMIN_ROLE) { + UtilLib.checkNonZeroAddress(_ssvToken); + ssvToken = _ssvToken; + emit UpdatedSSVTokenAddress(_ssvToken); + } + + /** + * @notice update the address of SSV Network Contract + * @dev only ADMIN role can update + * @param _ssvNetwork new address of SSV Network Contract + */ + function updateSSVNetworkAddress(address _ssvNetwork) external onlyRole(DEFAULT_ADMIN_ROLE) { + UtilLib.checkNonZeroAddress(_ssvNetwork); + ssvNetwork = ISSVNetwork(_ssvNetwork); + emit UpdatedSSVNetworkContractAddress(_ssvNetwork); + } + + /** + * @notice update the address of SSV Network Views Contract + * @dev only ADMIN role can update + * @param _ssvNetworkViews new address of SSV Network Views Contract + */ + function updateSSVNetworkViewsAddress(address _ssvNetworkViews) external onlyRole(DEFAULT_ADMIN_ROLE) { + UtilLib.checkNonZeroAddress(_ssvNetworkViews); + ssvNetworkViews = ISSVNetworkViews(_ssvNetworkViews); + emit UpdatedSSVNetworkViewsContractAddress(_ssvNetworkViews); + } + // check for the validator being registered with SSV along with the PRE_DEPOSIT status function onlySSVRegisteredAndPreDepositValidator(bytes calldata _pubkey) external view { bool active = ssvNetworkViews.getValidator(address(this), _pubkey); @@ -552,7 +662,7 @@ contract SSVNodeRegistry is } function _verifyOperatorAndDepositAmount(uint256 _operatorId, uint256 _depositAmount) internal view { - if (_operatorId == 0 || operatorStructById[_operatorId].operatorType) { + if (_operatorId == 0 || operatorStructById[_operatorId].isPermissionedOperator) { revert OperatorNotOnboardOrPermissioned(); } if (_depositAmount % COLLATERAL_ETH_PER_KEY_SHARE != 0) { @@ -585,26 +695,33 @@ contract SSVNodeRegistry is for (uint64 j = 0; j < CLUSTER_SIZE; j++) { SSVOperator storage operator = operatorStructById[staderOperatorIds[j]]; bool enoughSDCollateral = ISDCollateral(staderConfig.getSDCollateral()).hasEnoughSDCollateral( - msg.sender, + operator.operatorAddress, POOL_ID, operator.keyShareCount + 1 ); - if (!operator.operatorType && (operator.bondAmount < COLLATERAL_ETH_PER_KEY_SHARE || !enoughSDCollateral)) { + if ( + !operator.isPermissionedOperator && + (operator.bondAmount < COLLATERAL_ETH_PER_KEY_SHARE || !enoughSDCollateral) + ) { revert NotSufficientCollateralPerKeyShare(); } - if (!operator.operatorType) { + if (!operator.isPermissionedOperator) { totalCollateral += COLLATERAL_ETH_PER_KEY_SHARE; + operator.bondAmount -= COLLATERAL_ETH_PER_KEY_SHARE; } - operator.bondAmount -= COLLATERAL_ETH_PER_KEY_SHARE; operator.keyShareCount += 1; - ssvOperatorIds[j] = operatorStructById[staderOperatorIds[j]].operatorSSVID; + ssvOperatorIds[j] = operator.operatorSSVID; } if (totalCollateral != COLLATERAL_ETH) { revert NotSufficientCollateralPerValidator(); } } - function _getSSVOperatorIds(uint256[] memory staderOperatorIds) internal returns (uint64[] memory ssvOperatorIds) { + function _getSSVOperatorIds(uint256[] memory staderOperatorIds) + internal + view + returns (uint64[] memory ssvOperatorIds) + { ssvOperatorIds = new uint64[](CLUSTER_SIZE); for (uint64 i = 0; i < CLUSTER_SIZE; i++) { ssvOperatorIds[i] = operatorStructById[staderOperatorIds[i]].operatorSSVID; diff --git a/contracts/DVT/SSV/SSVPool.sol b/contracts/DVT/SSV/SSVPool.sol index eb83506c..960d197f 100644 --- a/contracts/DVT/SSV/SSVPool.sol +++ b/contracts/DVT/SSV/SSVPool.sol @@ -100,15 +100,17 @@ contract SSVPool is ISSVPool, AccessControlUpgradeable, ReentrancyGuardUpgradeab } // deposit `FULL_DEPOSIT_SIZE` for the verified preDeposited Validator - function fullDepositOnBeaconChain(bytes[] calldata _pubkey) external nonReentrant { + function fullDepositOnBeaconChain(bytes[] calldata _pubkey) external payable nonReentrant { UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.SSV_NODE_REGISTRY()); address nodeRegistryAddress = msg.sender; address vaultFactory = staderConfig.getVaultFactory(); address ethDepositContract = staderConfig.getETHDepositContract(); uint256 pubkeyCount = _pubkey.length; + //decrease the preDeposit validator count _decreasePreDepositValidatorCount(pubkeyCount); for (uint256 i; i < pubkeyCount; ) { + //checks if validator is registered with SSV Network and status is PRE_DEPOSIT ISSVNodeRegistry(nodeRegistryAddress).onlySSVRegisteredAndPreDepositValidator(_pubkey[i]); uint256 validatorId = INodeRegistry(nodeRegistryAddress).validatorIdByPubkey(_pubkey[i]); (, , , bytes memory depositSignature, address withdrawVaultAddress, , , ) = INodeRegistry( @@ -132,7 +134,7 @@ contract SSVPool is ISSVPool, AccessControlUpgradeable, ReentrancyGuardUpgradeab depositSignature, depositDataRoot ); - // ISSVNodeRegistry(nodeRegistryAddress).updateDepositStatusAndBlock(validatorId); + ISSVNodeRegistry(nodeRegistryAddress).updateDepositStatusAndBlock(validatorId); emit ValidatorDepositedOnBeaconChain(validatorId, _pubkey[i]); unchecked { ++i; diff --git a/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol b/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol index 0e0d2ce9..3557c99e 100644 --- a/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol +++ b/contracts/DVT/SSV/SSVValidatorWithdrawalVault.sol @@ -49,9 +49,11 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { uint256[] memory operatorIds = nodeRegistry.getOperatorsIdsForValidatorId(validatorId); uint256 totalOperators = operatorIds.length; for (uint8 i; i < totalOperators; ) { - (bool operatorType, , , address operatorAddress, , , ) = nodeRegistry.operatorStructById(operatorIds[i]); + (bool isPermissionedOperator, , , address operatorAddress, , , ) = nodeRegistry.operatorStructById( + operatorIds[i] + ); uint256 operatorKeyLevelShare; - if (operatorType) { + if (isPermissionedOperator) { operatorKeyLevelShare = getPermissionedOperatorShare( operatorShare, totalRewards, @@ -142,10 +144,9 @@ contract SSVValidatorWithdrawalVault is ISSVValidatorWithdrawalVault { IStaderStakePoolManager(staderConfig.getStakePoolManager()).receiveWithdrawVaultUserShare{value: userShare}(); UtilLib.sendValue(payable(staderConfig.getStaderTreasury()), protocolShare); for (uint8 i; i < totalOperators; i++) { - (bool operatorType, , , address operatorAddress, , , ) = ISSVNodeRegistry(msg.sender).operatorStructById( - operatorIds[i] - ); - if (operatorType) { + (bool isPermissionedOperator, , , address operatorAddress, , , ) = ISSVNodeRegistry(msg.sender) + .operatorStructById(operatorIds[i]); + if (isPermissionedOperator) { IOperatorRewardsCollector(staderConfig.getOperatorRewardsCollector()).depositFor{ value: permissionedOperatorShare }(operatorAddress); diff --git a/contracts/SDCollateral.sol b/contracts/SDCollateral.sol index b3d8cec2..0b9af9a7 100644 --- a/contracts/SDCollateral.sol +++ b/contracts/SDCollateral.sol @@ -89,20 +89,18 @@ contract SDCollateral is ISDCollateral, AccessControlUpgradeable, ReentrancyGuar uint256 _validatorId, uint256[] memory operatorIds ) external override nonReentrant { - (, , , , address withdrawVaultAddress, , , ) = INodeRegistry( - IPoolUtils(staderConfig.getPoolUtils()).getNodeRegistry(_poolId) - ).validatorRegistry(_validatorId); + address nodeRegistry = IPoolUtils(staderConfig.getPoolUtils()).getNodeRegistry(_poolId); + (, , , , address withdrawVaultAddress, , , ) = INodeRegistry(nodeRegistry).validatorRegistry(_validatorId); if (msg.sender != withdrawVaultAddress) { revert CallerNotWithdrawVault(); } isPoolThresholdValid(_poolId); uint256 sdToSlash = convertETHToSD(poolThresholdbyPoolId[_poolId].minThreshold); for (uint8 i; i < operatorIds.length; i++) { - (bool operatorType, , , address operatorAddress, , , ) = ISSVNodeRegistry(msg.sender).operatorStructById( - operatorIds[i] - ); - if (!operatorType) { - slashSD(operatorAddress, convertETHToSD(sdToSlash)); + (bool isPermissionedOperator, , , address operatorAddress, , , ) = ISSVNodeRegistry(nodeRegistry) + .operatorStructById(operatorIds[i]); + if (!isPermissionedOperator) { + slashSD(operatorAddress, sdToSlash); } } } diff --git a/contracts/StaderInsuranceFund.sol b/contracts/StaderInsuranceFund.sol index d260f142..f479d4af 100644 --- a/contracts/StaderInsuranceFund.sol +++ b/contracts/StaderInsuranceFund.sol @@ -53,7 +53,7 @@ contract StaderInsuranceFund is IStaderInsuranceFund, AccessControlUpgradeable, * @param _amount amount of ETH to transfer to permissioned/SSV pool */ function reimburseUserFund(uint256 _amount) external override nonReentrant { - if (msg.sender != staderConfig.getPermissionedPool() || msg.sender != staderConfig.getSSVPool()) { + if (msg.sender != staderConfig.getPermissionedPool() && msg.sender != staderConfig.getSSVPool()) { revert InvalidPoolToReimburse(); } if (address(this).balance < _amount) { diff --git a/contracts/factory/VaultFactory.sol b/contracts/factory/VaultFactory.sol index 66dd1364..413cc7b7 100644 --- a/contracts/factory/VaultFactory.sol +++ b/contracts/factory/VaultFactory.sol @@ -93,7 +93,7 @@ contract VaultFactory is IVaultFactory, AccessControlUpgradeable { returns (address) { bytes32 salt = sha256(abi.encode(_poolId, _validatorId)); - return ClonesUpgradeable.predictDeterministicAddress(vaultProxyImplementation, salt); + return ClonesUpgradeable.predictDeterministicAddress(ssvVaultProxyImplementation, salt); } function computeNodeELRewardVaultAddress(uint8 _poolId, uint256 _operatorId) diff --git a/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol b/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol index eb18a87a..c77895dd 100644 --- a/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol +++ b/contracts/interfaces/DVT/SSV/ISSVNodeRegistry.sol @@ -6,7 +6,7 @@ import '../../INodeRegistry.sol'; import '../../../library/ValidatorStatus.sol'; struct SSVOperator { - bool operatorType; // 0 for permissionless and 1 for permissioned + bool isPermissionedOperator; // false for permissionless and true for permissioned string operatorName; // name of the operator address payable operatorRewardAddress; //Eth1 address of node for reward address operatorAddress; // address of operator to interact with stader @@ -20,6 +20,7 @@ interface ISSVNodeRegistry { error PageNumberIsZero(); error UNEXPECTED_STATUS(); error InvalidBondAmount(); + error OperatorNotOnBoarded(); error DifferentClusterSize(); error ValidatorNotWithdrawn(); error InvalidCollateralAmount(); @@ -30,6 +31,8 @@ interface ISSVNodeRegistry { error CallerFailingSSVOperatorChecks(); error DuplicatePoolIDOrPoolNotAdded(); error ValidatorNotRegisteredWithSSV(); + error CallerNotExistingRewardAddress(); + error OnlyNewRewardAddressCanConfirm(); error OperatorNotOnboardOrPermissioned(); error OperatorAlreadyOnBoardedInProtocol(); error NotSufficientCollateralPerKeyShare(); @@ -37,22 +40,30 @@ interface ISSVNodeRegistry { event OperatorWhitelisted(address operator); event UpdatedStaderConfig(address staderConfig); + event UpdatedSSVTokenAddress(address ssvToken); event MarkedValidatorStatusAsPreDeposit(bytes pubkey); + event UpdatedSSVNetworkContractAddress(address ssvNetwork); event UpdatedInputKeyCountLimit(uint256 inputKeyCountLimit); event ValidatorWithdrawn(bytes pubkey, uint256 validatorId); event SSVOperatorOnboard(address operator, uint256 operatorId); event UpdatedVerifiedKeyBatchSize(uint256 verifiedKeysBatchSize); event BondDeposited(address operator, uint256 depositBondAmount); + event UpdatedSSVNetworkViewsContractAddress(address ssvNetworkViews); event ValidatorMarkedAsFrontRunned(bytes pubkey, uint256 validatorId); event UpdatedNextQueuedValidatorIndex(uint256 nextQueuedValidatorIndex); event IncreasedTotalActiveValidatorCount(uint256 totalActiveValidatorCount); event DecreasedTotalActiveValidatorCount(uint256 totalActiveValidatorCount); + event UpdatedOperatorName(address indexed nodeOperator, string operatorName); + event UpdatedValidatorDepositBlock(uint256 validatorId, uint256 depositBlock); event ValidatorStatusMarkedAsInvalidSignature(bytes pubkey, uint256 validatorId); + event AddedValidatorKey(address indexed nodeOperator, bytes pubkey, uint256 validatorId); event UpdatedBatchSizeToRemoveValidatorFromSSV(uint64 batchSizeToRemoveValidatorFromSSV); event UpdatedBatchSizeToRegisterValidatorWithSSV(uint64 batchSizeToRegisterValidatorFromSSV); + event InitiatedRewardAddressChange(address indexed nodeOperator, address indexed rewardAddress); + event UpdatedOperatorRewardAddress(address indexed nodeOperator, address indexed rewardAddress); - // function withdrawnValidators(bytes[] calldata _pubkeys) external; + function withdrawnValidators(bytes[] calldata _pubkeys) external; function markValidatorReadyToDeposit( bytes[] calldata _readyToDepositPubkey, @@ -75,22 +86,6 @@ interface ISSVNodeRegistry { uint256 withdrawnTime ); - // returns the operator struct given operator Id - function operatorStructById(uint256) - external - view - returns ( - bool operatorType, - string calldata operatorName, - address payable operatorRewardAddress, - address operatorAddress, - uint64 operatorSSVID, - uint64 keyShareCount, - uint256 bondAmount - ); - - function onlySSVRegisteredAndPreDepositValidator(bytes calldata _pubkey) external view; - function increaseTotalActiveValidatorCount(uint256 _count) external; function nextQueuedValidatorIndex() external view returns (uint256); @@ -103,14 +98,37 @@ interface ISSVNodeRegistry { function updateBatchSizeToRegisterValidatorFromSSV(uint64 _batchSizeToRegisterValidatorFromSSV) external; + function updateOperatorName(string calldata _operatorName) external; + + function confirmRewardAddressChange(address _operatorAddress) external; + function markValidatorStatusAsPreDeposit(bytes calldata _pubkey) external; + function updateDepositStatusAndBlock(uint256 _validatorId) external; + + function initiateRewardAddressChange(address _operatorAddress, address _rewardAddress) external; + + //Getter + + function operatorStructById(uint256) + external + view + returns ( + bool isPermissionedOperator, + string calldata operatorName, + address payable operatorRewardAddress, + address operatorAddress, + uint64 operatorSSVID, + uint64 keyShareCount, + uint256 bondAmount + ); + + function onlySSVRegisteredAndPreDepositValidator(bytes calldata _pubkey) external view; + function getAllActiveValidators(uint256 _pageNumber, uint256 _pageSize) external view returns (Validator[] memory); - // returns the total number of queued validators function getTotalQueuedValidatorCount() external view returns (uint256); - // returns the total number of active validators function getTotalActiveValidatorCount() external view returns (uint256); function getCollateralETH() external view returns (uint256); diff --git a/contracts/interfaces/DVT/SSV/ISSVPool.sol b/contracts/interfaces/DVT/SSV/ISSVPool.sol index dd7fe7f4..d3e4b3e0 100644 --- a/contracts/interfaces/DVT/SSV/ISSVPool.sol +++ b/contracts/interfaces/DVT/SSV/ISSVPool.sol @@ -11,7 +11,6 @@ interface ISSVPool { event ValidatorPreDepositedOnBeaconChain(bytes pubKey); event ValidatorDepositedOnBeaconChain(uint256 indexed validatorId, bytes pubKey); event UpdatedCommissionFees(uint256 protocolFee, uint256 operatorFee); - event ReceivedCollateralETH(uint256 amount); event UpdatedStaderConfig(address staderConfig); event ReceivedInsuranceFund(uint256 amount); event TransferredETHToSSPMForDefectiveKeys(uint256 amount); @@ -24,7 +23,7 @@ interface ISSVPool { function transferETHOfDefectiveKeysToSSPM(uint256 _defectiveKeyCount) external; - function fullDepositOnBeaconChain(bytes[] calldata _pubkey) external; + function fullDepositOnBeaconChain(bytes[] calldata _pubkey) external payable; //Getters diff --git a/package-lock.json b/package-lock.json index 8b8cc39b..3bf2f6af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,10 +14,12 @@ "@openzeppelin/contracts-upgradeable": "^4.8.0-rc.1", "@uniswap/v3-core": "^1.0.1", "@uniswap/v3-periphery": "^1.4.3", + "bignumber.js": "^9.1.2", "bytes32": "^0.0.3", "dotenv": "^16.0.3", "get-random-values": "^2.0.0", - "ssv-keys": "github:bloxapp/ssv-keys" + "ssv-keys": "github:bloxapp/ssv-keys", + "ssv-scanner": "github:bloxapp/ssv-scanner" }, "devDependencies": { "@nomicfoundation/hardhat-foundry": "^1.0.1", @@ -160,6 +162,17 @@ "ethereumjs-util": "^7.1.5" } }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/@ethereumjs/tx": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz", @@ -169,6 +182,66 @@ "ethereumjs-util": "^7.1.5" } }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, "node_modules/@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -957,6 +1030,28 @@ "rlp": "^2.2.3" } }, + "node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/hashes": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", @@ -1950,7 +2045,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", - "dev": true, "funding": [ { "type": "individual", @@ -2318,13 +2412,24 @@ } }, "node_modules/@types/bn.js": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", - "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-dkpZu0szUtn9UXTmw+e0AJFd4D2XAxDnsCLdc05SfqpqzPEBft8eQr8uaFitfo/dUUOZERaLec2hHMG87A4Dxg==", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "node_modules/@types/chai": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", @@ -2352,6 +2457,11 @@ "@types/node": "*" } }, + "node_modules/@types/figlet": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/figlet/-/figlet-1.5.6.tgz", + "integrity": "sha512-AOdn9cKJGXpqfHeif16xeGMwWefB4nsOyxkdRMpc+PEP3nUxzu3psJfIqhjrCNW4Sejt5i6rISWmEwK0sw03mA==" + }, "node_modules/@types/form-data": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", @@ -2373,6 +2483,19 @@ "@types/node": "*" } }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", + "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2435,6 +2558,14 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/secp256k1": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", @@ -2443,6 +2574,24 @@ "@types/node": "*" } }, + "node_modules/@types/underscore": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.9.tgz", + "integrity": "sha512-M63wKUdsjDFUfyFt1TCUZHGFk9KDAa5JP0adNUErbm0U45Lr06HtANdYRP+GyleEopEoZ4UyBcdAC5TnW4Uz2w==" + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, "node_modules/@uniswap/lib": { "version": "4.0.1-alpha", "resolved": "https://registry.npmjs.org/@uniswap/lib/-/lib-4.0.1-alpha.tgz", @@ -2514,6 +2663,11 @@ "node": ">=6.5" } }, + "node_modules/abortcontroller-polyfill": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", + "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==" + }, "node_modules/abstract-level": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", @@ -3020,9 +3174,9 @@ } }, "node_modules/bignumber.js": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", - "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "engines": { "node": "*" } @@ -3338,6 +3492,14 @@ "bytes32": "bin.js" } }, + "node_modules/cacheable-lookup": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", + "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", + "engines": { + "node": ">=10.6.0" + } + }, "node_modules/cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -3664,6 +3826,17 @@ "node": ">=4" } }, + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/cli-table3": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", @@ -4080,6 +4253,14 @@ "dev": true, "peer": true }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "node_modules/cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -4576,6 +4757,11 @@ "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, "node_modules/es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", @@ -6331,6 +6517,11 @@ "node": ">= 0.12" } }, + "node_modules/form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -6997,6 +7188,18 @@ "npm": ">=1.3.7" } }, + "node_modules/http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -7703,9 +7906,9 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, "node_modules/jsencrypt": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsencrypt/-/jsencrypt-3.3.1.tgz", - "integrity": "sha512-dVvV54GdFuJgmEKn+oBiaifDMen4p6o6j/lJh0OVMcouME8sST0bJ7bldIgKBQk4za0zyGn0/pm4vOznR25mLw==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsencrypt/-/jsencrypt-3.2.1.tgz", + "integrity": "sha512-k1sD5QV0KPn+D8uG9AdGzTQuamt82QZ3A3l6f7TRwMU6Oi2Vg0BsL+wZIQBONcraO1pc78ExMdvmBBJ8WhNYUA==" }, "node_modules/json-buffer": { "version": "3.0.0", @@ -8137,6 +8340,11 @@ "node": ">= 0.6" } }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -8644,6 +8852,25 @@ "semver": "bin/semver" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-gyp-build": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", @@ -9395,6 +9622,17 @@ } ] }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -9676,6 +9914,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "node_modules/resolve-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", @@ -10570,10 +10813,13 @@ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "node_modules/ssv-keys": { - "version": "0.0.4", - "resolved": "git+ssh://git@github.com/bloxapp/ssv-keys.git#24450d54668407a88cea2ec53fbe1c772a5acb46", + "version": "1.0.4", + "resolved": "git+ssh://git@github.com/bloxapp/ssv-keys.git#cc9e5cdd4696a0e855fc4642c2868abd62d5141a", "license": "MIT", "dependencies": { + "@types/figlet": "^1.5.4", + "@types/underscore": "^1.11.4", + "@types/yargs": "^17.0.12", "argparse": "^2.0.1", "assert": "^2.0.0", "atob": "^2.1.2", @@ -10584,17 +10830,19 @@ "colors": "^1.4.0", "crypto": "^1.0.1", "eth2-keystore-js": "^1.0.8", - "ethereumjs-util": "^7.0.10", + "ethereumjs-util": "^7.1.5", "ethereumjs-wallet": "^1.0.1", + "ethers": "^5.7.2", "events": "^3.3.0", "figlet": "^1.5.2", "js-base64": "^3.7.2", - "jsencrypt": "^3.2.1", + "jsencrypt": "3.2.1", "minimist": "^1.2.6", "moment": "^2.29.3", "node-jsencrypt": "^1.0.0", "prompts": "https://github.com/meshin-blox/prompts.git", "scrypt-js": "^3.0.1", + "semver": "^7.5.1", "stream": "^0.0.2", "underscore": "^1.13.4", "web3": "1.7.3", @@ -10664,6 +10912,31 @@ "xhr-request-promise": "^0.1.2" } }, + "node_modules/ssv-keys/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ssv-keys/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ssv-keys/node_modules/uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", @@ -10988,6 +11261,11 @@ "node": ">=8.0.0" } }, + "node_modules/ssv-keys/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/ssv-keys/node_modules/yargs": { "version": "17.6.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", @@ -11013,102 +11291,727 @@ "node": ">=12" } }, - "node_modules/stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", - "dev": true, + "node_modules/ssv-scanner": { + "version": "1.0.1", + "resolved": "git+ssh://git@github.com/bloxapp/ssv-scanner.git#872355cdecfab99606cedd077d1ff61ce95f4f04", + "license": "MIT", "dependencies": { - "type-fest": "^0.7.1" + "@types/figlet": "^1.5.4", + "argparse": "^2.0.1", + "cli-progress": "^3.11.2", + "figlet": "^1.5.2", + "web3": "^1.10.0" + }, + "bin": { + "ssv-keys": "dist/tsc/src/cli.js" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/ssv-scanner/node_modules/@ethereumjs/common": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.5.0.tgz", + "integrity": "sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==", + "dependencies": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.1" } }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" + "node_modules/ssv-scanner/node_modules/@ethereumjs/tx": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.2.tgz", + "integrity": "sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog==", + "dependencies": { + "@ethereumjs/common": "^2.5.0", + "ethereumjs-util": "^7.1.2" } }, - "node_modules/stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", - "dev": true, - "peer": true, + "node_modules/ssv-scanner/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", "engines": { - "node": ">=0.10.0" + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/stream": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", - "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", + "node_modules/ssv-scanner/node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", "dependencies": { - "emitter-component": "^1.1.1" + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, - "engines": { - "node": ">=10.0.0" + "node_modules/ssv-scanner/node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "node_modules/ssv-scanner/node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/ssv-scanner/node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dependencies": { - "safe-buffer": "~5.2.0" + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" } }, - "node_modules/string-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", - "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", - "dev": true + "node_modules/ssv-scanner/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/ssv-scanner/node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "dependencies": { + "node_modules/ssv-scanner/node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ssv-scanner/node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ssv-scanner/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ssv-scanner/node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ssv-scanner/node_modules/eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "dependencies": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "node_modules/ssv-scanner/node_modules/eth-lib/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ssv-scanner/node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/ssv-scanner/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ssv-scanner/node_modules/got": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", + "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "@szmarczak/http-timer": "^5.0.1", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/ssv-scanner/node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/ssv-scanner/node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/ssv-scanner/node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ssv-scanner/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ssv-scanner/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ssv-scanner/node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/ssv-scanner/node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ssv-scanner/node_modules/responselike/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ssv-scanner/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/ssv-scanner/node_modules/web3": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.10.2.tgz", + "integrity": "sha512-DAtZ3a3ruPziE80uZ3Ob0YDZxt6Vk2un/F5BcBrxO70owJ9Z1Y2+loZmbh1MoAmoLGjA/SUSHeUtid3fYmBaog==", + "hasInstallScript": true, + "dependencies": { + "web3-bzz": "1.10.2", + "web3-core": "1.10.2", + "web3-eth": "1.10.2", + "web3-eth-personal": "1.10.2", + "web3-net": "1.10.2", + "web3-shh": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-bzz": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.10.2.tgz", + "integrity": "sha512-vLOfDCj6198Qc7esDrCKeFA/M3ZLbowsaHQ0hIL4NmIHoq7lU8aSRTa5AI+JBh8cKN1gVryJsuW2ZCc5bM4I4Q==", + "hasInstallScript": true, + "dependencies": { + "@types/node": "^12.12.6", + "got": "12.1.0", + "swarm-js": "^0.1.40" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-core": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.10.2.tgz", + "integrity": "sha512-qTn2UmtE8tvwMRsC5pXVdHxrQ4uZ6jiLgF5DRUVtdi7dPUmX18Dp9uxKfIfhGcA011EAn8P6+X7r3pvi2YRxBw==", + "dependencies": { + "@types/bn.js": "^5.1.1", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-core-requestmanager": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-core-helpers": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.10.2.tgz", + "integrity": "sha512-1JfaNtox6/ZYJHNoI+QVc2ObgwEPeGF+YdxHZQ7aF5605BmlwM1Bk3A8xv6mg64jIRvEq1xX6k9oG6x7p1WgXQ==", + "dependencies": { + "web3-eth-iban": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-core-method": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.10.2.tgz", + "integrity": "sha512-gG6ES+LOuo01MJHML4gnEt702M8lcPGMYZoX8UjZzmEebGrPYOY9XccpCrsFgCeKgQzM12SVnlwwpMod1+lcLg==", + "dependencies": { + "@ethersproject/transactions": "^5.6.2", + "web3-core-helpers": "1.10.2", + "web3-core-promievent": "1.10.2", + "web3-core-subscriptions": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-core-promievent": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.10.2.tgz", + "integrity": "sha512-Qkkb1dCDOU8dZeORkcwJBQRAX+mdsjx8LqFBB+P4W9QgwMqyJ6LXda+y1XgyeEVeKEmY1RCeTq9Y94q1v62Sfw==", + "dependencies": { + "eventemitter3": "4.0.4" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-core-requestmanager": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.10.2.tgz", + "integrity": "sha512-nlLeNJUu6fR+ZbJr2k9Du/nN3VWwB4AJPY4r6nxUODAmykgJq57T21cLP/BEk6mbiFQYGE9TrrPhh4qWxQEtAw==", + "dependencies": { + "util": "^0.12.5", + "web3-core-helpers": "1.10.2", + "web3-providers-http": "1.10.2", + "web3-providers-ipc": "1.10.2", + "web3-providers-ws": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-core-subscriptions": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.10.2.tgz", + "integrity": "sha512-MiWcKjz4tco793EPPPLc/YOJmYUV3zAfxeQH/UVTfBejMfnNvmfwKa2SBKfPIvKQHz/xI5bV2TF15uvJEucU7w==", + "dependencies": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-eth": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.10.2.tgz", + "integrity": "sha512-s38rhrntyhGShmXC4R/aQtfkpcmev9c7iZwgb9CDIBFo7K8nrEJvqIOyajeZTxnDIiGzTJmrHxiKSadii5qTRg==", + "dependencies": { + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-core-subscriptions": "1.10.2", + "web3-eth-abi": "1.10.2", + "web3-eth-accounts": "1.10.2", + "web3-eth-contract": "1.10.2", + "web3-eth-ens": "1.10.2", + "web3-eth-iban": "1.10.2", + "web3-eth-personal": "1.10.2", + "web3-net": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-eth-abi": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.10.2.tgz", + "integrity": "sha512-pY4fQUio7W7ZRSLf+vsYkaxJqaT/jHcALZjIxy+uBQaYAJ3t6zpQqMZkJB3Dw7HUODRJ1yI0NPEFGTnkYf/17A==", + "dependencies": { + "@ethersproject/abi": "^5.6.3", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-eth-accounts": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.10.2.tgz", + "integrity": "sha512-6/HhCBYAXN/f553/SyxS9gY62NbLgpD1zJpENcvRTDpJN3Znvli1cmpl5Q3ZIUJkvHnG//48EWfWh0cbb3fbKQ==", + "dependencies": { + "@ethereumjs/common": "2.5.0", + "@ethereumjs/tx": "3.3.2", + "@ethereumjs/util": "^8.1.0", + "eth-lib": "0.2.8", + "scrypt-js": "^3.0.1", + "uuid": "^9.0.0", + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-eth-contract": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.10.2.tgz", + "integrity": "sha512-CZLKPQRmupP/+OZ5A/CBwWWkBiz5B/foOpARz0upMh1yjb0dEud4YzRW2gJaeNu0eGxDLsWVaXhUimJVGYprQw==", + "dependencies": { + "@types/bn.js": "^5.1.1", + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-core-promievent": "1.10.2", + "web3-core-subscriptions": "1.10.2", + "web3-eth-abi": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-eth-ens": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.10.2.tgz", + "integrity": "sha512-kTQ42UdNHy4BQJHgWe97bHNMkc3zCMBKKY7t636XOMxdI/lkRdIjdE5nQzt97VjQvSVasgIWYKRAtd8aRaiZiQ==", + "dependencies": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-promievent": "1.10.2", + "web3-eth-abi": "1.10.2", + "web3-eth-contract": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-eth-iban": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.10.2.tgz", + "integrity": "sha512-y8+Ii2XXdyHQMFNL2NWpBnXe+TVJ4ryvPlzNhObRRnIo4O4nLIXS010olLDMayozDzoUlmzCmBZJYc9Eev1g7A==", + "dependencies": { + "bn.js": "^5.2.1", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-eth-personal": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.10.2.tgz", + "integrity": "sha512-+vEbJsPUJc5J683y0c2aN645vXC+gPVlFVCQu4IjPvXzJrAtUfz26+IZ6AUOth4fDJPT0f1uSLS5W2yrUdw9BQ==", + "dependencies": { + "@types/node": "^12.12.6", + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-net": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-net": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.10.2.tgz", + "integrity": "sha512-w9i1t2z7dItagfskhaCKwpp6W3ylUR88gs68u820y5f8yfK5EbPmHc6c2lD8X9ZrTnmDoeOpIRCN/RFPtZCp+g==", + "dependencies": { + "web3-core": "1.10.2", + "web3-core-method": "1.10.2", + "web3-utils": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-providers-http": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.10.2.tgz", + "integrity": "sha512-G8abKtpkyKGpRVKvfjIF3I4O/epHP7mxXWN8mNMQLkQj1cjMFiZBZ13f+qI77lNJN7QOf6+LtNdKrhsTGU72TA==", + "dependencies": { + "abortcontroller-polyfill": "^1.7.5", + "cross-fetch": "^4.0.0", + "es6-promise": "^4.2.8", + "web3-core-helpers": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-providers-ipc": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.10.2.tgz", + "integrity": "sha512-lWbn6c+SgvhLymU8u4Ea/WOVC0Gqs7OJUvauejWz+iLycxeF0xFNyXnHVAi42ZJDPVI3vnfZotafoxcNNL7Sug==", + "dependencies": { + "oboe": "2.1.5", + "web3-core-helpers": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-providers-ws": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.10.2.tgz", + "integrity": "sha512-3nYSiP6grI5GvpkSoehctSywfCTodU21VY8bUtXyFHK/IVfDooNtMpd5lVIMvXVAlaxwwrCfjebokaJtKH2Iag==", + "dependencies": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.10.2", + "websocket": "^1.0.32" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-shh": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.10.2.tgz", + "integrity": "sha512-UP0Kc3pHv9uULFu0+LOVfPwKBSJ6B+sJ5KflF7NyBk6TvNRxlpF3hUhuaVDCjjB/dDUR6T0EQeg25FA2uzJbag==", + "hasInstallScript": true, + "dependencies": { + "web3-core": "1.10.2", + "web3-core-method": "1.10.2", + "web3-core-subscriptions": "1.10.2", + "web3-net": "1.10.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssv-scanner/node_modules/web3-utils": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.2.tgz", + "integrity": "sha512-TdApdzdse5YR+5GCX/b/vQnhhbj1KSAtfrDtRW7YS0kcWp1gkJsN62gw6GzCaNTeXookB7UrLtmDUuMv65qgow==", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dev": true, + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", + "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", + "dependencies": { + "emitter-component": "^1.1.1" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", "es-abstract": "^1.19.5" @@ -11489,6 +12392,11 @@ "node": ">=0.8" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-command-line-args": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz", @@ -12033,15 +12941,14 @@ "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" }, "node_modules/util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", "which-typed-array": "^1.1.2" } }, @@ -12515,6 +13422,11 @@ "node": ">=8.0.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, "node_modules/websocket": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", @@ -12544,6 +13456,15 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -13039,6 +13960,11 @@ "ethereumjs-util": "^7.1.5" } }, + "@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==" + }, "@ethereumjs/tx": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz", @@ -13048,6 +13974,53 @@ "ethereumjs-util": "^7.1.5" } }, + "@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "requires": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, + "@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "requires": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "requires": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "requires": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + } + } + }, "@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -13520,6 +14493,21 @@ } } }, + "@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "requires": { + "@noble/hashes": "1.3.1" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + } + } + }, "@noble/hashes": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", @@ -14286,8 +15274,7 @@ "@scure/base": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", - "dev": true + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" }, "@scure/bip32": { "version": "1.1.0", @@ -14591,13 +15578,24 @@ } }, "@types/bn.js": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", - "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-dkpZu0szUtn9UXTmw+e0AJFd4D2XAxDnsCLdc05SfqpqzPEBft8eQr8uaFitfo/dUUOZERaLec2hHMG87A4Dxg==", "requires": { "@types/node": "*" } }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "@types/chai": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", @@ -14625,6 +15623,11 @@ "@types/node": "*" } }, + "@types/figlet": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/figlet/-/figlet-1.5.6.tgz", + "integrity": "sha512-AOdn9cKJGXpqfHeif16xeGMwWefB4nsOyxkdRMpc+PEP3nUxzu3psJfIqhjrCNW4Sejt5i6rISWmEwK0sw03mA==" + }, "@types/form-data": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", @@ -14646,6 +15649,19 @@ "@types/node": "*" } }, + "@types/http-cache-semantics": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", + "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==" + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "requires": { + "@types/node": "*" + } + }, "@types/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", @@ -14710,6 +15726,14 @@ } } }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, "@types/secp256k1": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", @@ -14718,6 +15742,24 @@ "@types/node": "*" } }, + "@types/underscore": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.9.tgz", + "integrity": "sha512-M63wKUdsjDFUfyFt1TCUZHGFk9KDAa5JP0adNUErbm0U45Lr06HtANdYRP+GyleEopEoZ4UyBcdAC5TnW4Uz2w==" + }, + "@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, "@uniswap/lib": { "version": "4.0.1-alpha", "resolved": "https://registry.npmjs.org/@uniswap/lib/-/lib-4.0.1-alpha.tgz", @@ -14773,6 +15815,11 @@ "event-target-shim": "^5.0.0" } }, + "abortcontroller-polyfill": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", + "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==" + }, "abstract-level": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", @@ -15157,9 +16204,9 @@ "dev": true }, "bignumber.js": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", - "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==" + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" }, "binary-extensions": { "version": "2.2.0", @@ -15421,6 +16468,11 @@ "resolved": "https://registry.npmjs.org/bytes32/-/bytes32-0.0.3.tgz", "integrity": "sha512-uQM5zGcelcBEk0R7vkIRVN7GnrDd2S5EeWrxv7dSFLPVoI5FwYiSYOOLaFwnsav0oSr3hZQ6mFq6QNi8Vt6n8w==" }, + "cacheable-lookup": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", + "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==" + }, "cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -15670,6 +16722,14 @@ "restore-cursor": "^2.0.0" } }, + "cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "requires": { + "string-width": "^4.2.3" + } + }, "cli-table3": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", @@ -16024,6 +17084,14 @@ "dev": true, "peer": true }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "requires": { + "node-fetch": "^2.6.12" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -16426,6 +17494,11 @@ "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, "es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", @@ -17874,6 +18947,11 @@ "mime-types": "^2.1.12" } }, + "form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -18381,6 +19459,15 @@ "sshpk": "^1.7.0" } }, + "http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + } + }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -18868,9 +19955,9 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, "jsencrypt": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsencrypt/-/jsencrypt-3.3.1.tgz", - "integrity": "sha512-dVvV54GdFuJgmEKn+oBiaifDMen4p6o6j/lJh0OVMcouME8sST0bJ7bldIgKBQk4za0zyGn0/pm4vOznR25mLw==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsencrypt/-/jsencrypt-3.2.1.tgz", + "integrity": "sha512-k1sD5QV0KPn+D8uG9AdGzTQuamt82QZ3A3l6f7TRwMU6Oi2Vg0BsL+wZIQBONcraO1pc78ExMdvmBBJ8WhNYUA==" }, "json-buffer": { "version": "3.0.0", @@ -19209,6 +20296,11 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, + "micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" + }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -19610,6 +20702,14 @@ } } }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "node-gyp-build": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", @@ -20167,6 +21267,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -20380,6 +21485,11 @@ "path-parse": "^1.0.6" } }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "resolve-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", @@ -21075,9 +22185,12 @@ } }, "ssv-keys": { - "version": "git+ssh://git@github.com/bloxapp/ssv-keys.git#24450d54668407a88cea2ec53fbe1c772a5acb46", + "version": "git+ssh://git@github.com/bloxapp/ssv-keys.git#cc9e5cdd4696a0e855fc4642c2868abd62d5141a", "from": "ssv-keys@github:bloxapp/ssv-keys", "requires": { + "@types/figlet": "^1.5.4", + "@types/underscore": "^1.11.4", + "@types/yargs": "^17.0.12", "argparse": "^2.0.1", "assert": "^2.0.0", "atob": "^2.1.2", @@ -21088,17 +22201,19 @@ "colors": "^1.4.0", "crypto": "^1.0.1", "eth2-keystore-js": "^1.0.8", - "ethereumjs-util": "^7.0.10", + "ethereumjs-util": "^7.1.5", "ethereumjs-wallet": "^1.0.1", + "ethers": "^5.7.2", "events": "^3.3.0", "figlet": "^1.5.2", "js-base64": "^3.7.2", - "jsencrypt": "^3.2.1", + "jsencrypt": "3.2.1", "minimist": "^1.2.6", "moment": "^2.29.3", "node-jsencrypt": "^1.0.0", "prompts": "https://github.com/meshin-blox/prompts.git", "scrypt-js": "^3.0.1", + "semver": "^7.5.1", "stream": "^0.0.2", "underscore": "^1.13.4", "web3": "1.7.3", @@ -21159,6 +22274,22 @@ "xhr-request-promise": "^0.1.2" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", @@ -21413,6 +22544,11 @@ "utf8": "3.0.0" } }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "yargs": { "version": "17.6.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", @@ -21434,6 +22570,478 @@ } } }, + "ssv-scanner": { + "version": "git+ssh://git@github.com/bloxapp/ssv-scanner.git#872355cdecfab99606cedd077d1ff61ce95f4f04", + "from": "ssv-scanner@github:bloxapp/ssv-scanner", + "requires": { + "@types/figlet": "^1.5.4", + "argparse": "^2.0.1", + "cli-progress": "^3.11.2", + "figlet": "^1.5.2", + "web3": "^1.10.0" + }, + "dependencies": { + "@ethereumjs/common": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.5.0.tgz", + "integrity": "sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==", + "requires": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.1" + } + }, + "@ethereumjs/tx": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.2.tgz", + "integrity": "sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog==", + "requires": { + "@ethereumjs/common": "^2.5.0", + "ethereumjs-util": "^7.1.2" + } + }, + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, + "@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "requires": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "requires": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" + }, + "@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "requires": { + "defer-to-connect": "^2.0.1" + } + }, + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "requires": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "got": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", + "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "requires": { + "@sindresorhus/is": "^4.6.0", + "@szmarczak/http-timer": "^5.0.1", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^2.0.0" + } + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" + }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==" + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "requires": { + "lowercase-keys": "^2.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + }, + "web3": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.10.2.tgz", + "integrity": "sha512-DAtZ3a3ruPziE80uZ3Ob0YDZxt6Vk2un/F5BcBrxO70owJ9Z1Y2+loZmbh1MoAmoLGjA/SUSHeUtid3fYmBaog==", + "requires": { + "web3-bzz": "1.10.2", + "web3-core": "1.10.2", + "web3-eth": "1.10.2", + "web3-eth-personal": "1.10.2", + "web3-net": "1.10.2", + "web3-shh": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-bzz": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.10.2.tgz", + "integrity": "sha512-vLOfDCj6198Qc7esDrCKeFA/M3ZLbowsaHQ0hIL4NmIHoq7lU8aSRTa5AI+JBh8cKN1gVryJsuW2ZCc5bM4I4Q==", + "requires": { + "@types/node": "^12.12.6", + "got": "12.1.0", + "swarm-js": "^0.1.40" + } + }, + "web3-core": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.10.2.tgz", + "integrity": "sha512-qTn2UmtE8tvwMRsC5pXVdHxrQ4uZ6jiLgF5DRUVtdi7dPUmX18Dp9uxKfIfhGcA011EAn8P6+X7r3pvi2YRxBw==", + "requires": { + "@types/bn.js": "^5.1.1", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-core-requestmanager": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-core-helpers": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.10.2.tgz", + "integrity": "sha512-1JfaNtox6/ZYJHNoI+QVc2ObgwEPeGF+YdxHZQ7aF5605BmlwM1Bk3A8xv6mg64jIRvEq1xX6k9oG6x7p1WgXQ==", + "requires": { + "web3-eth-iban": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-core-method": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.10.2.tgz", + "integrity": "sha512-gG6ES+LOuo01MJHML4gnEt702M8lcPGMYZoX8UjZzmEebGrPYOY9XccpCrsFgCeKgQzM12SVnlwwpMod1+lcLg==", + "requires": { + "@ethersproject/transactions": "^5.6.2", + "web3-core-helpers": "1.10.2", + "web3-core-promievent": "1.10.2", + "web3-core-subscriptions": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-core-promievent": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.10.2.tgz", + "integrity": "sha512-Qkkb1dCDOU8dZeORkcwJBQRAX+mdsjx8LqFBB+P4W9QgwMqyJ6LXda+y1XgyeEVeKEmY1RCeTq9Y94q1v62Sfw==", + "requires": { + "eventemitter3": "4.0.4" + } + }, + "web3-core-requestmanager": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.10.2.tgz", + "integrity": "sha512-nlLeNJUu6fR+ZbJr2k9Du/nN3VWwB4AJPY4r6nxUODAmykgJq57T21cLP/BEk6mbiFQYGE9TrrPhh4qWxQEtAw==", + "requires": { + "util": "^0.12.5", + "web3-core-helpers": "1.10.2", + "web3-providers-http": "1.10.2", + "web3-providers-ipc": "1.10.2", + "web3-providers-ws": "1.10.2" + } + }, + "web3-core-subscriptions": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.10.2.tgz", + "integrity": "sha512-MiWcKjz4tco793EPPPLc/YOJmYUV3zAfxeQH/UVTfBejMfnNvmfwKa2SBKfPIvKQHz/xI5bV2TF15uvJEucU7w==", + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.10.2" + } + }, + "web3-eth": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.10.2.tgz", + "integrity": "sha512-s38rhrntyhGShmXC4R/aQtfkpcmev9c7iZwgb9CDIBFo7K8nrEJvqIOyajeZTxnDIiGzTJmrHxiKSadii5qTRg==", + "requires": { + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-core-subscriptions": "1.10.2", + "web3-eth-abi": "1.10.2", + "web3-eth-accounts": "1.10.2", + "web3-eth-contract": "1.10.2", + "web3-eth-ens": "1.10.2", + "web3-eth-iban": "1.10.2", + "web3-eth-personal": "1.10.2", + "web3-net": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-eth-abi": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.10.2.tgz", + "integrity": "sha512-pY4fQUio7W7ZRSLf+vsYkaxJqaT/jHcALZjIxy+uBQaYAJ3t6zpQqMZkJB3Dw7HUODRJ1yI0NPEFGTnkYf/17A==", + "requires": { + "@ethersproject/abi": "^5.6.3", + "web3-utils": "1.10.2" + } + }, + "web3-eth-accounts": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.10.2.tgz", + "integrity": "sha512-6/HhCBYAXN/f553/SyxS9gY62NbLgpD1zJpENcvRTDpJN3Znvli1cmpl5Q3ZIUJkvHnG//48EWfWh0cbb3fbKQ==", + "requires": { + "@ethereumjs/common": "2.5.0", + "@ethereumjs/tx": "3.3.2", + "@ethereumjs/util": "^8.1.0", + "eth-lib": "0.2.8", + "scrypt-js": "^3.0.1", + "uuid": "^9.0.0", + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-eth-contract": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.10.2.tgz", + "integrity": "sha512-CZLKPQRmupP/+OZ5A/CBwWWkBiz5B/foOpARz0upMh1yjb0dEud4YzRW2gJaeNu0eGxDLsWVaXhUimJVGYprQw==", + "requires": { + "@types/bn.js": "^5.1.1", + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-core-promievent": "1.10.2", + "web3-core-subscriptions": "1.10.2", + "web3-eth-abi": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-eth-ens": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.10.2.tgz", + "integrity": "sha512-kTQ42UdNHy4BQJHgWe97bHNMkc3zCMBKKY7t636XOMxdI/lkRdIjdE5nQzt97VjQvSVasgIWYKRAtd8aRaiZiQ==", + "requires": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-promievent": "1.10.2", + "web3-eth-abi": "1.10.2", + "web3-eth-contract": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-eth-iban": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.10.2.tgz", + "integrity": "sha512-y8+Ii2XXdyHQMFNL2NWpBnXe+TVJ4ryvPlzNhObRRnIo4O4nLIXS010olLDMayozDzoUlmzCmBZJYc9Eev1g7A==", + "requires": { + "bn.js": "^5.2.1", + "web3-utils": "1.10.2" + } + }, + "web3-eth-personal": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.10.2.tgz", + "integrity": "sha512-+vEbJsPUJc5J683y0c2aN645vXC+gPVlFVCQu4IjPvXzJrAtUfz26+IZ6AUOth4fDJPT0f1uSLS5W2yrUdw9BQ==", + "requires": { + "@types/node": "^12.12.6", + "web3-core": "1.10.2", + "web3-core-helpers": "1.10.2", + "web3-core-method": "1.10.2", + "web3-net": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-net": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.10.2.tgz", + "integrity": "sha512-w9i1t2z7dItagfskhaCKwpp6W3ylUR88gs68u820y5f8yfK5EbPmHc6c2lD8X9ZrTnmDoeOpIRCN/RFPtZCp+g==", + "requires": { + "web3-core": "1.10.2", + "web3-core-method": "1.10.2", + "web3-utils": "1.10.2" + } + }, + "web3-providers-http": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.10.2.tgz", + "integrity": "sha512-G8abKtpkyKGpRVKvfjIF3I4O/epHP7mxXWN8mNMQLkQj1cjMFiZBZ13f+qI77lNJN7QOf6+LtNdKrhsTGU72TA==", + "requires": { + "abortcontroller-polyfill": "^1.7.5", + "cross-fetch": "^4.0.0", + "es6-promise": "^4.2.8", + "web3-core-helpers": "1.10.2" + } + }, + "web3-providers-ipc": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.10.2.tgz", + "integrity": "sha512-lWbn6c+SgvhLymU8u4Ea/WOVC0Gqs7OJUvauejWz+iLycxeF0xFNyXnHVAi42ZJDPVI3vnfZotafoxcNNL7Sug==", + "requires": { + "oboe": "2.1.5", + "web3-core-helpers": "1.10.2" + } + }, + "web3-providers-ws": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.10.2.tgz", + "integrity": "sha512-3nYSiP6grI5GvpkSoehctSywfCTodU21VY8bUtXyFHK/IVfDooNtMpd5lVIMvXVAlaxwwrCfjebokaJtKH2Iag==", + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.10.2", + "websocket": "^1.0.32" + } + }, + "web3-shh": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.10.2.tgz", + "integrity": "sha512-UP0Kc3pHv9uULFu0+LOVfPwKBSJ6B+sJ5KflF7NyBk6TvNRxlpF3hUhuaVDCjjB/dDUR6T0EQeg25FA2uzJbag==", + "requires": { + "web3-core": "1.10.2", + "web3-core-method": "1.10.2", + "web3-core-subscriptions": "1.10.2", + "web3-net": "1.10.2" + } + }, + "web3-utils": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.2.tgz", + "integrity": "sha512-TdApdzdse5YR+5GCX/b/vQnhhbj1KSAtfrDtRW7YS0kcWp1gkJsN62gw6GzCaNTeXookB7UrLtmDUuMv65qgow==", + "requires": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + } + } + } + }, "stacktrace-parser": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", @@ -21816,6 +23424,11 @@ "punycode": "^2.1.1" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "ts-command-line-args": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz", @@ -22208,15 +23821,14 @@ "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" }, "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", "which-typed-array": "^1.1.2" } }, @@ -22613,6 +24225,11 @@ "utf8": "3.0.0" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, "websocket": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", @@ -22641,6 +24258,15 @@ } } }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index 861d8e9a..01870852 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,11 @@ "@openzeppelin/contracts-upgradeable": "^4.8.0-rc.1", "@uniswap/v3-core": "^1.0.1", "@uniswap/v3-periphery": "^1.4.3", + "bignumber.js": "^9.1.2", "bytes32": "^0.0.3", "dotenv": "^16.0.3", "get-random-values": "^2.0.0", - "ssv-keys": "github:bloxapp/ssv-keys" + "ssv-keys": "github:bloxapp/ssv-keys", + "ssv-scanner": "github:bloxapp/ssv-scanner" } } diff --git a/scripts/KeyShareGeneration/clusterInfo.ts b/scripts/KeyShareGeneration/clusterInfo.ts new file mode 100644 index 00000000..3d113472 --- /dev/null +++ b/scripts/KeyShareGeneration/clusterInfo.ts @@ -0,0 +1,27 @@ +import { ClusterScanner, NonceScanner } from 'ssv-scanner'; + +async function main() { + // these parameters should be known in advance + const params = { + nodeUrl: process.env.PROVIDER_URL_INFURA ?? '', // this can be an Infura, or Alchemy node, necessary to query the blockchain + contractAddress: process.env.SSV_NETWORK ?? '', // this is the address of SSV smart contract + ownerAddress: process.env.SSV_NODE_REGISTRY ?? '', // this is the wallet address of the cluster owner + operatorIds: [181,184,185,186], // this is a list of operator IDs chosen by the owner for their cluster + } + + // ClusterScanner is initialized with the given parameters + const clusterScanner = new ClusterScanner(params); + // and when run, it returns the Cluster Snapshot + const result = await clusterScanner.run(params.operatorIds); + console.log(JSON.stringify({ + 'block': result.payload.Block, + 'cluster snapshot': result.cluster, + 'cluster': Object.values(result.cluster) + }, null, ' ')); + + const nonceScanner = new NonceScanner(params); + const nextNonce = await nonceScanner.run(); + console.log('Next Nonce:', nextNonce); +} + +void main(); \ No newline at end of file diff --git a/scripts/KeyShareGeneration/keyShareSplit.ts b/scripts/KeyShareGeneration/keyShareSplit.ts new file mode 100644 index 00000000..be62af1f --- /dev/null +++ b/scripts/KeyShareGeneration/keyShareSplit.ts @@ -0,0 +1,62 @@ +const { SSVKeys, KeyShares } = require('ssv-keys'); +const path = require('path'); +const fsp = require('fs').promises; + +// These would probably come from your DApp +const operatorKeys = ["LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBc3BNREoxTWlPMFFmYWtGeTVqSzYKZDNnd1BOajN6WG1yMmtQS1U3R3VVb0pyQUF2NEI2d1RmZnJtdlFwY2JjczAwc0o4ZWtsb1NwME8wK0xJcHUrZgphVkRuUm5tK1ZBSFI4VTB4NWRPT2p1RzVybE1RaUJrQjE5ekErOTJSTDBOT2FiR1dMUUN6R0NVYnpCcnNJTXNVCnNrbUhBRHFabEY5RWI3a1ZWNVVNaEF1cUhGRkJwODJqMHRCZ1JOK0pUeU5NVGlMa2dIZzZSZ1AzSDIxOXVxNDYKb0lCVmk2ZllXZ3JEM0FDS2JVdEVxL1ZGVE1zWUhWakYvQTdFMDg1R2VhUnNnVmJKdzlSSjVJUkRKMGFpcStPOAplOU5QU3JYd2c5Uit1WEJGSWFrYkYwc0ZENE9nZk84YUFWNlJ1cHIyUFJOOXhpcmZlbTBqVzNQQjVEWjAxNzQrCjJ3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K", +"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcThoVEFyQVEwYkRxNEI3NjI5SEQKNnBZSUJqSGhsT3RIUTJtTkpBNENTWnVYZVNkRU1wY3l1U3l5VE80MTJSKzlPdWZwdS9QUnpiM2J1YVplWUU4bgpXbnVzQ3NDQUxnNW8wd0dIOFZ0U3FzWmptN0xQeGtpR0NZQnMyZjFxUjZveUM0SXc0ZzlDWHloYmE0T0xuZlBNCk1oaGJKYUl1RTRUazZyVHpXSGQ2YVlmOXY2WjVvMXp5K2ZTUTlnOHRJNnJLNmx6Q20vbkFucDQrblp0NDZzR0kKcTZiWk1WUzM0Q3FDSHFzMmk2dnh0dy9sTHNXMlFzVnEzaVRheFMvR0k5VUZtQmVnUnVnYmNjWVhiNlIrNS94RQpCL0dCTkhxSEFNRjBvd0JGdmVHc21BcXVEZWdpb0t1c2pVTXNmVm5YSTBmdGJPbXQ3NXB1QnNiZDVyNjhNdkdwCkR3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K", + "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBb0Zwcmdmc09tVmNmK085NUR5ODEKUi93YUZoWUJta1BJWVhmdUpFTzdYN2tWUlhXa3BlMWM4dFFveE5zdEJYYm95d1g3aWpmcWZDRjZGd3VVZVN1WApESXZtNGl0aXpMTTJwY3ZsWDNBSTBNLzZoTElDV21YTGsyekxydU9sVGRwMjJYTFVTWU12VEVnRVRMT0NQZ3k0CjM0bXA1Rm5xcXhtb2xQSHhCa3BzOTNHOWswYTFSZGllRVJVZDdqMmpEUjNRaVpwNHkwTDFqRitubzdhbnZOSmUKVUM2SXExMnNWUldscmM0VExOUS9iOHNLejJ4NS9WQnBNalpzQnF2VS9WaE1kc2hacmdlWXNNNm43bHRwaG5NQQpzcS9CNk9IRVg5dXg5MEV0Nmk1TDBnWFZNQ3paN29ZRlBWUk03RjBmdko3cHRQVkhtMTUyZHdHekJNQnVnazFPCkhRSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K", + "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcGtEdGhmc3Zka1JXWFBJdFBWUS8KVTBRVlNzcjBPWEMwWlBYL3NrTjNjbTRDUXN4WDgzL2drNGxoUGRQQys4OTVGcDliWmN3cXZNSnRFRTZvUlVpNgpyWGgrdUN0aUFtVG9EMFFuYld2eER0bEhWZFY3NkdnSDIvZE5ORTVPRDFjamZhVlIvaW9YUWNCZGVlSzB3L2dZCm9NL1pValNya1lDQXNORmx3SHFwYlQxUFM2aGdoY0w1cjN2TEp2RXkrMCt1clYxYjh0blh0MEl1aUxWTU1SRDkKSlptRW9ieUhtMGd6ZnZjOGgyaC8wUDlLbVN5Q3NCSUk3VG5XcUZ6OUFvZWp5K3ltdUZYSHBCcHNlbUQ2VGJRVAppeHpQcm9INkNpQVpjRWlhZFRsemd5dEF2ZXhVQlB1STNlaDhVSmtsYzkza2NGQU1NWCtnejNDYTRrYWM1MlFUCnlRSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K"]; +const operatorIds = [181, 184, 185, 186]; +// These can be either provided by the user (Staking-as-a-Service) or auto-generated (Staking Pool) +const keystore = require('./keystore4.json'); +const keystorePassword = ''; + +// The nonce of the owner within the SSV contract (increments after each validator registration), obtained using the ssv-scanner tool +const TEST_OWNER_NONCE = 3; +// The cluster owner address +const TEST_OWNER_ADDRESS = '0xc97A3092dd785e1b65155Dd56664dD358B981e2d'; + +const getKeySharesFilePath = () => { + return `${path.join(process.cwd(), 'data')}${path.sep}keyshares.json`; +}; + +/** + * This is more complex example demonstrating usage of SSVKeys SDK together with + * KeyShares file which can be useful in a different flows for solo staker, staking provider or web developer. + */ +async function main() { + // 1. Initialize SSVKeys SDK and read the keystore file + const ssvKeys = new SSVKeys(); + const { publicKey, privateKey } = await ssvKeys.extractKeys(keystore, keystorePassword); + + const operators = operatorKeys.map((operatorKey: any, index: number) => ({ + id: operatorIds[index], + operatorKey, + })); + + // 2. Build shares from operator IDs and public keys + const encryptedShares = await ssvKeys.buildShares(privateKey, operators); + + const keyShares = new KeyShares(); + await keyShares.update({ operators }); + await keyShares.update({ ownerAddress: TEST_OWNER_ADDRESS, ownerNonce: TEST_OWNER_NONCE, publicKey }); + + // 3. Build final web3 transaction payload and update keyshares file with payload data + const payload = await keyShares.buildPayload({ + publicKey, + operators, + encryptedShares, + }, { + ownerAddress: TEST_OWNER_ADDRESS, + ownerNonce: TEST_OWNER_NONCE, + privateKey + }); + + console.log('payload is ', payload) + + // Most times, you'd want to save the result in a file +// await fsp.writeFile(getKeySharesFilePath(4), keyShares.toJson(), { encoding: 'utf-8' }); +} + +void main(); \ No newline at end of file diff --git a/scripts/addValidatorKeys.ts b/scripts/addValidatorKeys.ts index da977875..b338ba05 100644 --- a/scripts/addValidatorKeys.ts +++ b/scripts/addValidatorKeys.ts @@ -1,13 +1,13 @@ import { ethers } from 'hardhat' async function main() { - const permissionlessNodeRegistry = process.env.PERMISSIONLESS_NODE_REGISTRY ?? '' - const permissionlessNodeRegistryFactory = await ethers.getContractFactory('PermissionlessNodeRegistry') - const permissionlessNodeRegistryInstance = await permissionlessNodeRegistryFactory.attach(permissionlessNodeRegistry) + const ssvNodeRegistry = process.env.SSV_NODE_REGISTRY ?? '' + const ssvNodeRegistryFactory = await ethers.getContractFactory('SSVNodeRegistry') + const ssvNodeRegistryInstance = await ssvNodeRegistryFactory.attach(ssvNodeRegistry) - const addKeyTx = await permissionlessNodeRegistryInstance.addValidatorKeys([], [], [], { - value: ethers.utils.parseEther(''), - }) + const addKeyTx = await ssvNodeRegistryInstance.addValidatorKeys(['0x8cff5fbae2555829c91a35f8d8cd6218371c8706e0324c617d8cdb27e04f7494cd33a1d484ceb48992770a11113b68ff'], + ['0xb6ee3561b8c84737a14768a4e1e03f998de6e4a064a608ec56291c6d73151e459a6a8f96c5e8ff4d52f6df0ef0ecdd7f17af7352c32746c97f9f7154d0f7dec8f4ab023f2d72d5a49cd0f8e74846b860a8f9b0ebb020133c8b32889df52b8f76'], + ['0x919435e354be5b878371892afa03295fd981a987795b841d79f8398876535158a2866ce5694995bc0c7ae118f4cb63760f9e005fa8c47e8bea3dfe625ed3701932c64fd6b635d871dd59b25c08eee27e7fee3b1be5b834c9eef8538f8cb57bf6']) console.log('added validator keys') } diff --git a/scripts/customError.ts b/scripts/customError.ts index c4925dee..2c3d44d1 100644 --- a/scripts/customError.ts +++ b/scripts/customError.ts @@ -2,10 +2,10 @@ import { Interface } from '@ethersproject/abi' const axios = require('axios').default async function errorDecode() { - const permissionlessNodeRegistryJson = require('../artifacts/contracts/PermissionedNodeRegistry.sol/PermissionedNodeRegistry.json') + const permissionlessNodeRegistryJson = require('../artifacts/contracts/DVT/SSV/SSVNodeRegistry.sol/SSVNodeRegistry.json') let interfaces = new Interface(permissionlessNodeRegistryJson.abi) - let error_msg = interfaces.getError('0xc4a3c169') + let error_msg = interfaces.getError('0x1adfa873') console.log('error is ', error_msg.name) } diff --git a/scripts/deployContractWithSSV.ts b/scripts/deployContractWithSSV.ts new file mode 100644 index 00000000..d3814273 --- /dev/null +++ b/scripts/deployContractWithSSV.ts @@ -0,0 +1,152 @@ +import { ethers, upgrades } from 'hardhat' + +async function main() { + console.log('starting deployment process...') + const staderAdmin = process.env.STADER_ADMIN ?? '' + const externalAdmin = process.env.EXTERNAL_ADMIN ?? '' + //check for network and change accordingly + const ethDepositContract = process.env.ETH_DEPOSIT_CONTRACT ?? '' + const ratedOracle = process.env.RATED ?? '' + const ssvNetwork = process.env.SSV_NETWORK ?? '' + const ssvNetworkView = process.env.SSV_NETWORK_VIEW ?? '' + const staderConfig = process.env.STADER_CONFIG ?? '' + + // const StaderConfig = await ethers.getContractFactory('StaderConfig') + // const staderConfig = await upgrades.deployProxy(StaderConfig, [staderAdmin, ethDepositContract]) + // console.log('stader config deployed at ', staderConfig.address) + + // const vaultFactory = await ethers.getContractFactory('VaultFactory') + // const vaultFactoryInstance = await upgrades.deployProxy(vaultFactory, [externalAdmin, staderConfig.address]) + // console.log('vaultFactoryInstance deployed at ', vaultFactoryInstance.address) + + // const auctionFactory = await ethers.getContractFactory('Auction') + // const auctionInstance = await upgrades.deployProxy(auctionFactory, [externalAdmin, staderConfig.address]) + // console.log('auction contract deployed at ', auctionInstance.address) + + // const ETHxFactory = await ethers.getContractFactory('ETHx') + // const ETHxToken = await upgrades.deployProxy(ETHxFactory, [externalAdmin, staderConfig.address]) + // console.log('ETHx deployed at ', ETHxToken.address) + + // const operatorRewardCollectorFactory = await ethers.getContractFactory('OperatorRewardsCollector') + // const operatorRewardCollector = await upgrades.deployProxy(operatorRewardCollectorFactory, [ + // externalAdmin, + // staderConfig.address, + // ]) + // console.log('operator reward collector at ', operatorRewardCollector.address) + + // const penaltyFactory = await ethers.getContractFactory('Penalty') + // const penaltyInstance = await upgrades.deployProxy(penaltyFactory, [externalAdmin, staderConfig.address, ratedOracle]) + // console.log('penalty contract deployed at ', penaltyInstance.address) + + // const PermissionedNodeRegistryFactory = await ethers.getContractFactory('PermissionedNodeRegistry') + // const permissionedNodeRegistry = await upgrades.deployProxy(PermissionedNodeRegistryFactory, [ + // externalAdmin, + // staderConfig.address, + // ]) + // console.log('permissionedNodeRegistry deployed at ', permissionedNodeRegistry.address) + + // const permissinedPoolFactory = await ethers.getContractFactory('PermissionedPool') + // const permissionedPool = await upgrades.deployProxy(permissinedPoolFactory, [externalAdmin, staderConfig.address]) + // console.log('permissionedPool deployed at ', permissionedPool.address) + + // const PermissionlessNodeRegistryFactory = await ethers.getContractFactory('PermissionlessNodeRegistry') + // const permissionlessNodeRegistry = await upgrades.deployProxy(PermissionlessNodeRegistryFactory, [ + // externalAdmin, + // staderConfig.address, + // ]) + // console.log('permissionlessNodeRegistry deployed at ', permissionlessNodeRegistry.address) + + // const permissionlessPoolFactory = await ethers.getContractFactory('PermissionlessPool') + // const permissionlessPool = await upgrades.deployProxy(permissionlessPoolFactory, [ + // externalAdmin, + // staderConfig.address, + // ]) + // console.log('permissionlessPool deployed at ', permissionlessPool.address) + + // const SSVNodeRegistryFactory = await ethers.getContractFactory('SSVNodeRegistry') + // const SSVNodeRegistryRegistry = await upgrades.deployProxy(SSVNodeRegistryFactory, [ + // externalAdmin, + // staderConfig.address, + // ssvNetwork, + // ssvNetworkView + // ]) + // console.log('SSVNodeRegistryRegistry deployed at ', SSVNodeRegistryRegistry.address) + + // const ssvPoolFactory = await ethers.getContractFactory('SSVPool') + // const ssvPool = await upgrades.deployProxy(ssvPoolFactory, [ + // externalAdmin, + // staderConfig.address, + // ]) + // console.log('SSVPool deployed at ', ssvPool.address) + + // const poolSelectorFactory = await ethers.getContractFactory('PoolSelector') + // const poolSelector = await upgrades.deployProxy(poolSelectorFactory, [externalAdmin, staderConfig.address]) + // console.log('poolSelector deployed at ', poolSelector.address) + + // const poolUtilsFactory = await ethers.getContractFactory('PoolUtils') + // const poolUtilsInstance = await upgrades.deployProxy(poolUtilsFactory, [externalAdmin, staderConfig.address]) + // console.log('poolUtils deployed at ', poolUtilsInstance.address) + + // const SDCollateralFactory = await ethers.getContractFactory('SDCollateral') + // const SDCollateral = await upgrades.deployProxy(SDCollateralFactory, [externalAdmin, staderConfig.address]) + // console.log('SDCollateral deployed at ', SDCollateral.address) + + // const socializingPoolFactory = await ethers.getContractFactory('SocializingPool') + // const permissionedSocializingPoolContract = await upgrades.deployProxy(socializingPoolFactory, [ + // externalAdmin, + // staderConfig.address, + // ]) + // console.log('permissioned socializingPoolContract deployed at ', permissionedSocializingPoolContract.address) + + // const permissionlessSocializingPoolContract = await upgrades.deployProxy(socializingPoolFactory, [ + // externalAdmin, + // staderConfig.address, + // ]) + // console.log('permissionless socializingPoolContract deployed at ', permissionlessSocializingPoolContract.address) + + // const insuranceFundFactory = await ethers.getContractFactory('StaderInsuranceFund') + // const insuranceFund = await upgrades.deployProxy(insuranceFundFactory, [externalAdmin, staderConfig.address]) + // console.log('insurance fund deployed at ', insuranceFund.address) + + const staderOracleFactory = await ethers.getContractFactory('StaderOracle') + const staderOracle = await upgrades.deployProxy(staderOracleFactory, [externalAdmin, staderConfig]) + console.log('stader oracle deployed at ', staderOracle.address) + + // const poolManagerFactory = await ethers.getContractFactory('StaderStakePoolsManager') + // const staderStakingPoolManager = await upgrades.deployProxy(poolManagerFactory, [externalAdmin, staderConfig.address]) + // console.log('staderStakingPoolManager deployed at ', staderStakingPoolManager.address) + + // const userWithdrawFactory = await ethers.getContractFactory('UserWithdrawalManager') + // const userWithdrawManager = await upgrades.deployProxy(userWithdrawFactory, [externalAdmin, staderConfig.address]) + // console.log('userWithdrawManager deployed at ', userWithdrawManager.address) + + // const NodeELRewardVault = await ethers.getContractFactory('NodeELRewardVault') + // const nodeELRewardVault = await NodeELRewardVault.deploy() + // await nodeELRewardVault.deployed() + // console.log('nodeELRewardVault ', nodeELRewardVault.address) + + // const ValidatorWithdrawalVault = await ethers.getContractFactory('ValidatorWithdrawalVault') + // const validatorWithdrawalVault = await ValidatorWithdrawalVault.deploy() + // await validatorWithdrawalVault.deployed() + // console.log('validatorWithdrawalVault ', validatorWithdrawalVault.address) + + // const ssvSocializingPoolContract = await upgrades.deployProxy(socializingPoolFactory, [ + // externalAdmin, + // staderConfig.address, + // ]) + // console.log('SSV socializingPoolContract deployed at ', ssvSocializingPoolContract.address) + + // const SSVValidatorWithdrawalVault = await ethers.getContractFactory('SSVValidatorWithdrawalVault') + // const ssvValidatorVault = await SSVValidatorWithdrawalVault.deploy() + // await ssvValidatorVault.deployed() + // console.log('ssvValidatorVault ', ssvValidatorVault.address) + + + // const SSVValidatorProxy = await ethers.getContractFactory('SSVVaultProxy') + // const ssvProxyImpl = await SSVValidatorProxy.deploy() + // await ssvProxyImpl.deployed() + // console.log('ssvVault Proxy Impl ', ssvProxyImpl.address) + +} + +main() diff --git a/scripts/distributeRewards.ts b/scripts/distributeRewards.ts new file mode 100644 index 00000000..56f54a0d --- /dev/null +++ b/scripts/distributeRewards.ts @@ -0,0 +1,11 @@ +import { ethers } from 'hardhat' + +async function main() { + const poolManagerFactory = await ethers.getContractFactory('SSVValidatorWithdrawalVault') + const poolManagerInstance = await poolManagerFactory.attach('0xfD376Eb205023e682Fb5B107c1f37A46e3dD4Cda') + const depositTx = await poolManagerInstance.distributeRewards() + depositTx.wait() + console.log('distributed reward successfully') +} + +main() diff --git a/scripts/registerValidatorWithSSV.ts b/scripts/registerValidatorWithSSV.ts new file mode 100644 index 00000000..ec3182f1 --- /dev/null +++ b/scripts/registerValidatorWithSSV.ts @@ -0,0 +1,53 @@ +import { ethers } from 'hardhat' +import BigNumber from 'bignumber.js'; + +async function main() { + const ssVNodeRegistry = process.env.SSV_NODE_REGISTRY ?? '' + const ssvNodeRegistryFactory = await ethers.getContractFactory('SSVNodeRegistry') + const ssvNodeRegistryInstance = await ssvNodeRegistryFactory.attach(ssVNodeRegistry) + + interface Cluster { + validatorCount: number + networkFeeIndex: number + index: number + active: boolean + balance: BigNumber + } + + const input1: Cluster = { + validatorCount: 1, + networkFeeIndex: 0, + index: 0, + active: true, + balance: ethers.utils.parseEther("10"), + } + + const input2: Cluster = { + validatorCount: 2, + networkFeeIndex: 0, + index: 0, + active: true, + balance: ethers.utils.parseEther("20"), + } + + const input3: Cluster = { + validatorCount: 3, + networkFeeIndex: 0, + index: 0, + active: true, + balance: ethers.utils.parseEther("30"), + } + + const addKeyTx = await ssvNodeRegistryInstance.registerValidatorsWithSSV( + ethers.utils.parseEther("30"), + ['0x8ffa518de86de59dd92a7559c1d19dfb92961e8954aa601fe30502aec61af7b55c544366c1ba78fe8e03cdc57d9fbb12','0xa2fd2ba4f1d0166afb7120e8c63481ad2ecf00b898abe2558cd88a699a6cfb854455bd5cb3824078975bf13d8f9fb0ec', + '0x98badff3fc308773a59149b147cfee61e0c01be5518f292a3a9a2a0cdb60bc68fc3113a7530e7fa3b7618401e337e528'], + [[1,4,3,2],[1,4,3,2],[1,4,3,2]], + ['0xacfca8a175f28ec1660fef798446edda38049ca8e2598346e0d0d86c65930e6d27f63df5ba46b787c0d04551436713790a1252d23eb1056e35b10cd2e8866a8332d527382f8bc9efb0e9b6ebcc7d1d48073203c972784b4016cbf6f4ffe4db9bb137619a3898c975e7ff1ee2149bcb031f7fd2f9583a4d16eb3636764cf268794085284ba8bcba07cf6dd62dd3c0a8b1b467cdcd0d87278745f0966e480e15019751b46d785234176c468d6e53fd9151473666241b2cf2ab7a8f097f56a1590787729f9ed2863fe401a401c6a54880c41182b97944850377646a0256335ccdcb8c578ca1d915125bac6ef095f2ab250aa3212d61652a17005cdac9f5d9e997cfbff44d4c2a59f625cb0f48023d158883df5ccbea80d9dbfd4c38fe9a31ff1cd99547e5ffa4561bb45c74b231562b2686beeb6443dea6aeb947586e5cc12b1729c35f9a26f741929a27b42aabde3a848f6f90d3b769a3c885776fa2e9b5752680f06a93c21c87ff5f8dac9ed24c856c0704518637b5a5dd50049b9048ea9d5159d1e2a4f5db45704511acb5181c0f255951701fa4df8b85c8185f3f962e8c75bc37c3f716a52bef2794120d3207d7558302525412848d6894e27b0353aee53aa80d2a13b960d4d48c47abdfd55581d9c6daf220d7675bf1c0843e9fbf427051da969bdf5c9293d35d2be02e944b16ec6e7920312bf0b7ec9475058a47c4a19c3a06d17950a5ac66c7c549d18737065243afaab5079c2421ee229c21701fa0623f67e37584effd9deaa27be3f0176fae25dbdb64c3309d851a853d09256135d7b5a6399ee0272f05414128da167610a8878a4649b7b9bed717f6ba50e5d3e617d54fea3127d5fdc26fa0bf935f94863161fe7955a1769bcfb10452453e16c8c19f1a45d9a10ea81d09c452a152d2d715c164cee29fc721f1f80f3703125f1837bd95c393239daf69b6d680e77e072f95f8b77b535792ea7a7d960dbc6389958ccaa67cca05e36472a96f34c795b4a990794f22b10b396ad6f27e14890359a700bffc6327ee5a625427d8e918c6302f598d1abf063734da656f9ee95ac305cd301ea9a06a9b6858bea3aa61b871e861df8d6665d8b0f81b7c7af663b0f0ce63cba2379e24c1a0965ea1a349c0703d0df1519c8ebdd89a4d4862202536d2ef46f636ad9eec2178c6027223fa9c220441b35eaf96d722b9323cef96e8e71e5ef48b836998d9ca043cdc87ee32ca1064deca3c17790d97a7ce0f6ae16a5dbcc94e0d4b092f7ac760eb74c3890dd3cc276e5c45fd4e115030cb64ecdfa26eaeebb3026a622233293ceb15cf5a9bcf559ea59ecd9bb0be2854d6a81e83dc5195ed99d7b79c363061509a0a0bf91cbd5198b933cb93f903d439552a3854245bb568555ef6ed16b2b952769151fc55343f760118ccfc468a84c0298f582b8e56bd5c66a2f1a9a76b6654768be77b91c7dc72c7e48459f93e9a9c7d4e03c4ba6794ae4a13dd2a55d1a8291aea2574932fd6e8f8a166ec8307068ba7989cc673314a8dacb06eaa676555de01af434d1fc2e2d47e594bab2bc7e1794bdf739ca1fd2afa889f05fc53de8efc7b3958b4a6338fd53b284138e1c08723d9a93f14a16b44c8e8d5cbd5216b1227b890c4b456bb90c7bc88fe5fe887805a6ae90dd49857a4e463134dc8e9d2a6053db31071b7ca3558a1815d23022a77b45b5fa2779dda54688e7c403c00f2b7944e321f6cadb7ab5eb8faf1789ad91cd6bed0637b4ed9521abd4b5b1c525c2a1364a602c64f876af6d740b6bf29465ae766ccbd545f7f7879ca9d9c1268f22bd701c62a42b7df25ec8b5e4ef795df9836f9f05852d619adf33cec46', + '0x90e08d9aafcffec2b9a54ed4734d9f7080e3be87d74dc5aea422ccbed2876dd7b33e5f346c9d4e8c78ca75f72c0c2bba19fcf7c5639c67091bde4f400c7fd3c9c9c205ebf230a3c159185f2e6d420efeb72698d65c2a1ee56667a4782f3ad9a0b8aecc4fc3ad95142d61de1be033a3981e1789011e72ba52370775a16fb0fbf5abc1b0cf51ed9a56cefbe5b050069e96a60246d47854d077187c65eeb88392461697dff7a825f7c7e58dc05c96eb6949b5f2c0851fac2921b0b9b08118919849a8de7f485564ca07abe699c9f17d6aff4a4b21737b4588afc851734eb87b0e956d9687d6e5433c1da1d67b0387418b5ba4b922e2e8350573d2e19de880be7f4ea17a396521ae9c7cd1aee49fcd895801b1ea8b6d88682d9172761931416228cc35a1d2f545bc0db2d8762bcb907bce65dd69a0140546011d13a937a085612ed41644c4f191fd784aada4be660cdef4e2ae1493f29c23fd4d2b46a21f01b05ac0ae53852caa555e69485a4c00e8181648799349fa0ac7f2f3c6a141bad9a062dee27ebf9f8bf303f0afcd8b7ad2854046e53dc7fd8c5ddbd7d6533e9e4acea4c694b7d2c161d32fa59b7079d4dd9942b3eba28bfb821c5189f50baf34ffdaf49015832ff72e5ee2db18dcea7f5f8914d7b65ed2998c2f39643232f59a533960c6d60e91e4b30f3e5c49737af3634da1eb23fd974b1d0dff6033f9e15e52c67013fdf97ace987e24e4a2f953851abc5a641b038f15bcc6a5d58341207303f505254797ac22be8d13cd7466131ffcf787a1dfccf3d280fbbe6812c76af7703e00df2b695228a695e5b742e86c8b548f29555039c8ccb747c712e5a5caf35306dd2521f3f3ec6de289f2966cb5cd4c8458dbf68aff8231a5ec285d500b143d1752c9e3be9b08cc94ef1299e42d1196d210e1e85022e00637778b40cba17e7a1fc665b844be3d080f6070790c48ec5c511138f69a2d126da140376b624c2f831cb7180f09e8d52f6a0e73d3b9c1e28c5dddb371d9ca19ef2a25300bcfa3a2f3acf673b01402445aa998f949c9e1ee58c15bdfcf717b0554a734f0fbe0f531040b01ec05c50f3267f96c0b6837fb32f286fc5fc60a00d1adb451bd112229bb6a4d715a7f465d8b0203ff8e44ce8fc5dbf46a1636091c0c3e8839074068a33a97c44a165eb8afc65ab56fd60b9d2e58f9d5dc49587b36ae5f1619b2a645df7a62b135781fbc56710fb7b0887d99c332416b0900fb71a770137f831b3cb63240839dfbfc857945fd0c46fbb27a49359df1df1a07ccc6db4e9a1fb3693ea15ecc3e81c08b15d43cb1b58ce0744e9c36dc3afb33cad8f0a9c3e4df5344855b792bff292cb459d17da9d7488d365126f91f37492fe324d7e21ac01833813409b2fa65b81e9899fff7a9aedb46139df57927ef847adcb435bf8237f4ffbc38aec40948820fc2afe81fee8e27491ebc696ec63ced17b89ec0f1527f95c918b17590e86ee72592888b74ce231d7ae9b333068fdd1654bf5261dc47ec5e934aaab100dc5d136a42e23680fd8ab2e59454c9f0c0531957e320a24d86d581a5896aa187c87e533777e671ccd39c87545f25d21621a82f97cdd858eda473ebbdafe89a3c4e8fd888ef78bff15325e0e3671a866875feecd40de6a429299a4f84b0dc1e70906dd1b4a9ffb4b4a58213338d1efc12a3b1c4deb0255741d59aec5fcf9f1c7e6096d9f01857ec04b169be1de18cbc1d970aea0bcb7103320cc12cb1dd2f27ca1c1e94c041e5947a7ccefd7db2fa06d52115db1e7b2cf206c2ddf502403e0b41f520ebbe6df1bf96dfd5bd918c8b1cc0402cc8911d3c548c7b078f007f84f9aa855750648e', + '0xb4361ea7ea1c68b1e026929ffd79361995c7c50c056255ab32f0c575f5b76da037e0d91427086399dd5948c5298910c313f38ed3e43dc8830d177112bea506f8a0f644d07513c5c284d395630de8207d84ab8b1e9304b5adaca4225352a705cbb2c3fc95574401ea25db36405cd3d0d6a11ce5c5d0df801b9caad8f80fed2e62e7026bbcab0f50659ed66e2b00d00e78915e65278bf56bf13a42e7d60d220485eae54b1d8428f77123b4469b51b1f416d6bcff1e25563547b88ee8bdb4d3c18788ae6f3de82e73ab425b06032e6fc2e1bda1c0e7880795aded89f35b1a63bb6124701aa032eaada3c28e2ccec635f64198850ed40e682e0605dba39422d8b7e03b07cac26a7f31b344bbe7ceb44c787c7c56ada1dda9788e3f8f300c580d1d416b5cb5162f63a8712dcad6469790cf4a9bcfa614b76c68deca1b8676edaed5a8c336796046f7b9448772ca2ea9158dbe0ed8483b20ba1aaa94685670283c368c129c13110ac9100bb454d7f2514e2c2412a595f7cf5779845200fa580e11b618c7a484b002f9debb659a84c6264653261497efa5d0c7599d0483017d6744d53fbe38db0bab73c61c06a96cc21611a4ad8ea53cf2e37d74de67f40ab8ae2aa69ca841467bee803ecbf9161aeeedeea253be1c411ddc6ddcbbbedbda537db3eb0520a7f35b8e4a047696941efdd95077c91280a43e2d40e3cfa9f5efc243013108695f58d6551ff097a0690895068db905d364d7f5cf57da6ac02ac470fa3046ac93597df4861fd07ffc6e36d50a391a656fcc650d3e75467a6f0bc95e757e3452bc3c1a75db8117ee4815ed3aaeed61bd0c40b061d4b258daf348bb4111624050d060e605dcbd395a8359df3c36c9443444396d8dba32f4b13c9873161e5598f0dc51cd63c8730bb327bf54f8a5f1ed46206598869309cbc64790ab48ea446ef0a624683bb496ba0e8711558347f37113988aa5b38d8e0186a6b0037e9f4f47abf19a7a4ece0cec244afa6f5a6bf4df218acf2d6fc493934c95a47d7d5811c396854351439140e25d306424f284085c77734fcca8607390103c887294e66b059426c08fb377bfc26070a0d69dcf9ef3c9a5886dac1fbef0cfc1e2bee274b3ae2462c10b0e16a4b5a4f21d1bdb3a765d782e005344490dd4d5080f8e5931aaa52cc22281b429c542505c28a0994adc6ed91e40557892dfeaf3df8cbdf47649e6d1db486d24c21ed53c3e147f70ea3bcfc4f3e5cc7ff5c93defc3c1d72b5dd9bc750e6b01ba5304b1671206e7fec3a1e0d7fb45626dc21c91160c4bbe8df3825318d1679a2ddb1fcb409aff618f0b2be622da166dea9aa58d33662918f236238761540c1537da1905e4811c2acabc3f3f1ed274574d21172e1d2d59679e9cbba59a3fe02cb0b76f3683a1581a0380a9f2ca337bc83c8275a1061b65395ed7c15f102fbcf82e87abbc226f7eba35a1504de3a85397b8d282cfd5725675d0e381602e3cba2ca11937ad53c66568322be079d5a8a60583ddf0d44a9f70229103ca8d8f7496dce09d8aefff5ee4e167b646b33f1f50dd0b407434d98a58e29c13e2b3413ac0a480d6b2caf96fcf7184a7a2aabfde804c7aca22ec44dbadacedf3c755db22c289801ad44da4c5de5c93e8d5bc1e97c4c770d35b9645ebabc0024281e513a4b6888dc369c886f00c81defc75f2fdec1f410411d9ca6a764ce366e6b7d2855857cfc27391dbf64fcb87fddc9b8a236a2c89d32aa126021d170f2fca5e60425014cf6904cffe632d8293c28120560f232ad140f275e0c953a49038f5262ed1a5b5cfa08b094dfa1af42778f5fedea543e6d0edd96a2259b1d440c17cdee5fc'], + [input1,input2,input3]) + + console.log('added validator keys') +} +main() diff --git a/scripts/removeValidatorFromSSV.ts b/scripts/removeValidatorFromSSV.ts new file mode 100644 index 00000000..7507d2e6 --- /dev/null +++ b/scripts/removeValidatorFromSSV.ts @@ -0,0 +1,41 @@ +import { ethers } from 'hardhat' +import BigNumber from 'bignumber.js'; + +async function main() { + const ssVNodeRegistry = process.env.SSV_NODE_REGISTRY ?? '' + const ssvNodeRegistryFactory = await ethers.getContractFactory('SSVNodeRegistry') + const ssvNodeRegistryInstance = await ssvNodeRegistryFactory.attach(ssVNodeRegistry) + + interface Cluster { + validatorCount: number + networkFeeIndex: number + index: number + active: boolean + balance: BigNumber + } + + + const input1: Cluster = { + validatorCount: 4, + networkFeeIndex: 0, + index: 0, + active: true, + balance: ethers.utils.parseEther("40"), + } + + const input2: Cluster = { + validatorCount: 3, + networkFeeIndex: 0, + index: 0, + active: true, + balance: ethers.utils.parseEther("40"), + } + + const addKeyTx = await ssvNodeRegistryInstance.removeValidatorFromSSVNetwork( + ['0x8cff5fbae2555829c91a35f8d8cd6218371c8706e0324c617d8cdb27e04f7494cd33a1d484ceb48992770a11113b68ff', + '0x8ffa518de86de59dd92a7559c1d19dfb92961e8954aa601fe30502aec61af7b55c544366c1ba78fe8e03cdc57d9fbb12'], + [input1,input2]) + + console.log('removed validator keys') +} +main() diff --git a/scripts/submitOracleReport.ts b/scripts/submitOracleReport.ts index 57209b84..6548ee4c 100644 --- a/scripts/submitOracleReport.ts +++ b/scripts/submitOracleReport.ts @@ -6,19 +6,36 @@ async function main() { const staderOracleInstance = await staderOracleFactory.attach(staderOracle) interface WithdrawnValidatorsStruct { + poolId: number reportingBlockNumber: number - nodeRegistry: string sortedPubkeys: string[] } const input: WithdrawnValidatorsStruct = { - reportingBlockNumber: 8840650, - nodeRegistry: '0x7a9F54B8B6Bb1DBED83e16Bb34257397358752df', + poolId: 3, + reportingBlockNumber: 9748800, sortedPubkeys: [ - '0xa7354412be74304f5627a2883c16eae488b92612a21692d5e9fc3ada31281f83a5303e646157434962d3b2cec49503b2', - ], + '0x8ffa518de86de59dd92a7559c1d19dfb92961e8954aa601fe30502aec61af7b55c544366c1ba78fe8e03cdc57d9fbb12'], } + interface ValidatorVerification { + poolId: number + reportingBlockNumber: number + sortedReadyToDepositPubkeys: string[] + sortedFrontRunPubkeys: string[] + sortedInvalidSignaturePubkeys: string[] + } + + const input1: ValidatorVerification = { + poolId: 3, + reportingBlockNumber: 9727800, + sortedReadyToDepositPubkeys: ['0x8ffa518de86de59dd92a7559c1d19dfb92961e8954aa601fe30502aec61af7b55c544366c1ba78fe8e03cdc57d9fbb12','0xa2fd2ba4f1d0166afb7120e8c63481ad2ecf00b898abe2558cd88a699a6cfb854455bd5cb3824078975bf13d8f9fb0ec','0x98badff3fc308773a59149b147cfee61e0c01be5518f292a3a9a2a0cdb60bc68fc3113a7530e7fa3b7618401e337e528'], + sortedFrontRunPubkeys: [], + sortedInvalidSignaturePubkeys: [] + } + + + const addKeyTx = await staderOracleInstance.submitWithdrawnValidators(input) console.log('submitted oracle report') diff --git a/scripts/upgrade/ssvNodeRegistry.ts b/scripts/upgrade/ssvNodeRegistry.ts new file mode 100644 index 00000000..ea098c2c --- /dev/null +++ b/scripts/upgrade/ssvNodeRegistry.ts @@ -0,0 +1,15 @@ +import { ethers, upgrades } from 'hardhat' + +async function main() { + const ssvNodeRegistryAddress = process.env.SSV_NODE_REGISTRY ?? '' + const ssvNodeRegistryFactory = await ethers.getContractFactory('SSVNodeRegistry') + const ssvNodeRegistryInstance = await ssvNodeRegistryFactory.attach(ssvNodeRegistryAddress) + + const ssvNodeRegistryUpgraded = await upgrades.upgradeProxy(ssvNodeRegistryInstance, ssvNodeRegistryFactory) + + console.log('SSV NodeRegistry proxy address ', ssvNodeRegistryUpgraded.address) + + console.log('upgraded SSV NodeRegistry contract') +} + +main() diff --git a/scripts/upgrade/ssvPool.ts b/scripts/upgrade/ssvPool.ts new file mode 100644 index 00000000..593376f5 --- /dev/null +++ b/scripts/upgrade/ssvPool.ts @@ -0,0 +1,15 @@ +import { ethers, upgrades } from 'hardhat' + +async function main() { + const ssvPoolAddress = process.env.SSV_POOL ?? '' + const ssvPoolFactory = await ethers.getContractFactory('SSVPool') + const ssvPoolInstance = await ssvPoolFactory.attach(ssvPoolAddress) + + const ssvPoolUpgraded = await upgrades.upgradeProxy(ssvPoolInstance, ssvPoolFactory) + + console.log('SSV Pool proxy address ', ssvPoolUpgraded.address) + + console.log('upgraded SSV Pool contract') +} + +main() diff --git a/scripts/upgrade/staderInsuranceFund.ts b/scripts/upgrade/staderInsuranceFund.ts index ec1c8319..06441e86 100644 --- a/scripts/upgrade/staderInsuranceFund.ts +++ b/scripts/upgrade/staderInsuranceFund.ts @@ -1,7 +1,7 @@ import { ethers, upgrades } from 'hardhat' async function main() { - const insuranceFund = process.env.STADER_INSURANCE_FUND ?? '' + const insuranceFund = process.env.INSURANCE_FUND ?? '' const insuranceFundFactory = await ethers.getContractFactory('StaderInsuranceFund') const insuranceFundInstance = await insuranceFundFactory.attach(insuranceFund) diff --git a/scripts/verifyContracts.ts b/scripts/verifyContracts.ts index 3d7a59bf..095604a8 100644 --- a/scripts/verifyContracts.ts +++ b/scripts/verifyContracts.ts @@ -25,6 +25,13 @@ const userWithdrawManager = process.env.USER_WITHDRAW_MANAGER ?? '' const nodeELRewardVault = process.env.NODE_EL_REWARD_VAULT_IMPL ?? '' const withdrawVaultImpl = process.env.WITHDRAW_VAULT_IMPL ?? '' +//SSV Contracts +const ssvNodeRegistry = process.env.SSV_NODE_REGISTRY ?? '' +const ssvPool = process.env.SSV_POOL ?? '' +const ssvSocializingPool = process.env.SSV_SOCIALIZING_POOL ?? '' +const ssvWithdrawVault = process.env.SSV_WITHDRAWAL_VAULT ?? '' +const ssvVaultProxy = process.env.SSV_VAULT_PROXY ?? '' + const contractAddresses = [ vaultFactory, auction, @@ -47,6 +54,11 @@ const contractAddresses = [ userWithdrawManager, nodeELRewardVault, withdrawVaultImpl, + ssvNodeRegistry, + ssvPool, + ssvSocializingPool, + ssvWithdrawVault, + ssvVaultProxy ] async function main() {