From 15aea4fc1e1eb77da9e1b395f99507c834ad7a00 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Mon, 29 Apr 2024 15:52:26 +0200 Subject: [PATCH] SSV cluster info (#2036) * add ability to fetch SSV cluster information * prettier --- contracts/deploy/deployActions.js | 5 +- contracts/tasks/ssv.js | 83 ++++++++++++++++++++++++++++++ contracts/tasks/tasks.js | 37 ++++++++++++- contracts/utils/hardhat-helpers.js | 8 +++ 4 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 contracts/tasks/ssv.js diff --git a/contracts/deploy/deployActions.js b/contracts/deploy/deployActions.js index 536a6f19dd..88dab9238c 100644 --- a/contracts/deploy/deployActions.js +++ b/contracts/deploy/deployActions.js @@ -8,10 +8,7 @@ const { isMainnetOrFork, isHolesky, } = require("../test/helpers.js"); -const { - deployWithConfirmation, - withConfirmation, -} = require("../utils/deploy"); +const { deployWithConfirmation, withConfirmation } = require("../utils/deploy"); const { metapoolLPCRVPid, lusdMetapoolLPCRVPid, diff --git a/contracts/tasks/ssv.js b/contracts/tasks/ssv.js new file mode 100644 index 0000000000..539ef986cc --- /dev/null +++ b/contracts/tasks/ssv.js @@ -0,0 +1,83 @@ +const { ClusterScanner, NonceScanner } = require("ssv-scanner"); +const log = require("../utils/logger")("task:ssv"); + +const getClusterInfo = async ({ + ownerAddress, + operatorids, + chainId, + ssvNetwork, +}) => { + const operatorIds = operatorids.split(".").map((id) => parseInt(id)); + + const ssvNetworkName = chainId === 1 ? "MAINNET" : "PRATER"; + const providerUrl = + chainId === 1 ? process.env.PROVIDER_URL : process.env.HOLESKY_PROVIDER_URL; + + const params = { + nodeUrl: providerUrl, // this can be an Infura, or Alchemy node, necessary to query the blockchain + contractAddress: ssvNetwork, // this is the address of SSV smart contract + ownerAddress, // this is the wallet address of the cluster owner + /* Based on the network they fetch contract ABIs. See code: https://github.com/bloxapp/ssv-scanner/blob/v1.0.3/src/lib/contract.provider.ts#L16-L22 + * and the ABIs are fetched from here: https://github.com/bloxapp/ssv-scanner/tree/v1.0.3/src/shared/abi + * + * Prater seems to work for Goerli at the moment + */ + network: ssvNetworkName, + operatorIds: operatorIds, // 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); + const cluster = { + block: result.payload.Block, + snapshot: result.cluster, + cluster: Object.values(result.cluster), + }; + log(`Cluster info ${JSON.stringify(cluster)}`); + return cluster; +}; + +const getClusterNonce = async ({ + ownerAddress, + operatorids, + chainId, + ssvNetwork, +}) => { + const operatorIds = operatorids.split(".").map((id) => parseInt(id)); + + const ssvNetworkName = chainId === 1 ? "MAINNET" : "PRATER"; + const providerUrl = + chainId === 1 ? process.env.PROVIDER_URL : process.env.HOLESKY_PROVIDER_URL; + + const params = { + nodeUrl: providerUrl, // this can be an Infura, or Alchemy node, necessary to query the blockchain + contractAddress: ssvNetwork, // this is the address of SSV smart contract + ownerAddress, // this is the wallet address of the cluster owner + /* Based on the network they fetch contract ABIs. See code: https://github.com/bloxapp/ssv-scanner/blob/v1.0.3/src/lib/contract.provider.ts#L16-L22 + * and the ABIs are fetched from here: https://github.com/bloxapp/ssv-scanner/tree/v1.0.3/src/shared/abi + * + * Prater seems to work for Goerli at the moment + */ + network: ssvNetworkName, + operatorIds: operatorIds, // this is a list of operator IDs chosen by the owner for their cluster + }; + + const nonceScanner = new NonceScanner(params); + const nextNonce = await nonceScanner.run(); + return nextNonce; +}; + +const printClusterInfo = async (options) => { + const cluster = await getClusterInfo(options); + const nextNonce = await getClusterNonce(options); + console.log(`block ${cluster.block}`); + console.log(`Cluster: ${JSON.stringify(cluster.snapshot, null, " ")}`); + console.log("Next Nonce:", nextNonce); +}; + +module.exports = { + printClusterInfo, + getClusterInfo, +}; diff --git a/contracts/tasks/tasks.js b/contracts/tasks/tasks.js index e013cbd41b..6a6e08518d 100644 --- a/contracts/tasks/tasks.js +++ b/contracts/tasks/tasks.js @@ -1,10 +1,12 @@ const { subtask, task, types } = require("hardhat/config"); - const { fund } = require("./account"); const { debug } = require("./debug"); const { env } = require("./env"); const { execute, executeOnFork, proposal, governors } = require("./governance"); const { smokeTest, smokeTestCheck } = require("./smokeTest"); +const addresses = require("../utils/addresses"); +const { networkMap } = require("../utils/hardhat-helpers"); + const { storeStorageLayoutForAllContracts, assertStorageLayoutChangeSafe, @@ -46,6 +48,7 @@ const { curveSwapTask, curvePoolTask, } = require("./curve"); +const { printClusterInfo } = require("./ssv"); const { amoStrategyTask, mintAndAddOTokensTask, @@ -53,6 +56,7 @@ const { removeOnlyAssetsTask, } = require("./amoStrategy"); const { proxyUpgrades } = require("./proxy"); +const log = require("../utils/logger")("tasks"); // Environment tasks. task("env", "Check env vars are properly set for a Mainnet deployment", env); @@ -739,3 +743,34 @@ task("proxyUpgrades", "Lists all proxy implementation changes") types.int ) .setAction(proxyUpgrades); + +subtask("getClusterInfo", "Print out information regarding SSV cluster") + .addParam( + "operatorids", + "4 operator ids separated with a dot: same as IP format. E.g. 60.79.220.349", + "", + types.string + ) + .addParam( + "owner", + "Address of the cluster owner. Default to NodeDelegator", + undefined, + types.string + ) + .setAction(async (taskArgs) => { + const network = await ethers.provider.getNetwork(); + const ssvNetwork = addresses[networkMap[network.chainId]].SSVNetwork; + + log( + `Fetching cluster info for cluster owner ${taskArgs.owner} with operator ids: ${taskArgs.operatorids} from the ${network.name} network using ssvNetworkContract ${ssvNetwork}` + ); + await printClusterInfo({ + ...taskArgs, + ownerAddress: taskArgs.owner, + chainId: network.chainId, + ssvNetwork, + }); + }); +task("getClusterInfo").setAction(async (_, __, runSuper) => { + return runSuper(); +}); diff --git a/contracts/utils/hardhat-helpers.js b/contracts/utils/hardhat-helpers.js index cf6082afdb..9474f402ad 100644 --- a/contracts/utils/hardhat-helpers.js +++ b/contracts/utils/hardhat-helpers.js @@ -110,6 +110,13 @@ const getHardhatNetworkProperties = () => { return { chainId, provider }; }; +const networkMap = { + 1: "mainnet", + 17000: "holesky", + 42161: "arbitrumOne", + 1337: "hardhat", +}; + module.exports = { isFork, isArbitrumFork, @@ -123,4 +130,5 @@ module.exports = { holeskyProviderUrl, adjustTheForkBlockNumber, getHardhatNetworkProperties, + networkMap, };