From 8f767920f80af6cddf9bf5b58b6c2b2f0ada9c4e Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Tue, 7 May 2024 16:34:40 +0200 Subject: [PATCH 1/6] adding defender action task --- .gitignore | 1 + contracts/package.json | 1 + contracts/tasks/tasks.js | 64 ++++++++++++++++++++++++++++++++++++ contracts/tasks/validator.js | 16 +++++---- contracts/yarn.lock | 42 +++++++++++++++++++++-- 5 files changed, 115 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 37f3a25c43..f4a57a0d11 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ contracts/coverage/ contracts/coverage.json contracts/build/ contracts/dist/ +contracts/.localKeyValueStorage todo.txt brownie/env-brownie/ diff --git a/contracts/package.json b/contracts/package.json index a45dee4888..973416ddf3 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -55,6 +55,7 @@ "axios": "^1.4.0", "chai": "^4.3.4", "debug": "^4.3.4", + "defender-kvstore-client": "^1.38.1-rc.0", "dotenv": "^10.0.0", "eslint": "^7.32.0", "ethereum-waffle": "^4.0.10", diff --git a/contracts/tasks/tasks.js b/contracts/tasks/tasks.js index d922fdf25f..d0df225595 100644 --- a/contracts/tasks/tasks.js +++ b/contracts/tasks/tasks.js @@ -5,7 +5,9 @@ const { env } = require("./env"); const { execute, executeOnFork, proposal, governors } = require("./governance"); const { smokeTest, smokeTestCheck } = require("./smokeTest"); const addresses = require("../utils/addresses"); +const { getDefenderSigner } = require("../utils/signers"); const { networkMap } = require("../utils/hardhat-helpers"); +const { KeyValueStoreClient } = require("defender-kvstore-client"); const { storeStorageLayoutForAllContracts, @@ -879,3 +881,65 @@ subtask( task("depositSSV").setAction(async (_, __, runSuper) => { return runSuper(); }); + +// Defender +subtask("operateValidators", "Creates the required amount of new SSV validators and stakes ETH") + .addOptionalParam("index", "Index of Native Staking contract", 1, types.int) + .addOptionalParam("stake", "Stake 32 ether after registering a new SSV validator", true, types.boolean) + .addOptionalParam("days", "SSV Cluster operational time in days", 1, types.int) + .addOptionalParam("clear", "Clear storage", true, types.boolean) + .setAction(async (taskArgs) => { + const storeFilePath = require("path").join(__dirname, "..", ".localKeyValueStorage"); + const network = await ethers.provider.getNetwork(); + const isMainnet = network.chainId === 1; + const isHolesky = network.chainId === 17000; + + if (!isMainnet && !isHolesky) { + throw new Error("operate validatos is supported on Mainnet and Holesky only"); + } + + // TODO separate store file for a separate network + const store = new KeyValueStoreClient({ path: storeFilePath }); + const signer = await getDefenderSigner(); + + const WETH = await ethers.getContractAt("IWETH9", addresses.mainnet/holesky.WETH); + + const addressName = taskArgs.index === 1 ? "NODE_DELEGATOR_NATIVE_STAKING" : "NODE_DELEGATOR"; + const nodeDelegatorAddress = await parseAddress(addressName); + const nodeDelegator = await ethers.getContractAt("NodeDelegator", nodeDelegatorAddress); + + const contracts = { + nodeDelegator, + WETH, + }; + + const eigenPodAddress = await parseAddress("EIGEN_POD"); + + const p2p_api_key = network.chainId === 1 ? process.env.P2P_MAINNET_API_KEY : process.env.P2P_HOLESKY_API_KEY; + if (!p2p_api_key) { + throw new Error("P2P API key environment variable is not set. P2P_MAINNET_API_KEY or P2P_HOLESKY_API_KEY"); + } + const p2p_base_url = network.chainId === 1 ? "api.p2p.org" : "api-test-holesky.p2p.org"; + + const config = { + eigenPodAddress, + p2p_api_key, + p2p_base_url, + // how much SSV (expressed in days of runway) gets deposited into the + // SSV Network contract on validator registration. This is calculated + // at a Cluster level rather than a single validator. + validatorSpawnOperationalPeriodInDays: taskArgs.days, + stake: taskArgs.stake, + clear: taskArgs.clear, + }; + + await operateValidators({ + signer, + contracts, + store, + config, + }); + }); +task("operateValidators").setAction(async (_, __, runSuper) => { + return runSuper(); +}); diff --git a/contracts/tasks/validator.js b/contracts/tasks/validator.js index 566dbef27c..5bd5759be2 100644 --- a/contracts/tasks/validator.js +++ b/contracts/tasks/validator.js @@ -31,6 +31,8 @@ const ERROR_THRESHOLD = 5; * - if spawn process gets stuck at any of the above steps and is not able to * recover in X amount of times (e.g. 5 times). Mark the process as failed * and start over. + * - TODO: (implement this) if fuse of the native staking strategy is blown + * stop with all the operations */ const operateValidators = async ({ store, signer, contracts, config }) => { const { @@ -61,8 +63,8 @@ const operateValidators = async ({ store, signer, contracts, config }) => { await createValidatorRequest( p2p_api_key, // api key p2p_base_url, - contracts.nodeDelegator.address, // node delegator address - eigenPodAddress, // eigenPod address + nativeStakingStrategy.address, // node delegator address + feeRecipientAddress, // eigenPod address validatorSpawnOperationalPeriodInDays, store ); @@ -302,8 +304,8 @@ const p2pRequest = async (url, api_key, method, body) => { const createValidatorRequest = async ( p2p_api_key, p2p_base_url, - nodeDelegatorAddress, - eigenPodAddress, + nativeStakingStrategy, + feeRecipientAddress, validatorSpawnOperationalPeriodInDays, store ) => { @@ -315,9 +317,9 @@ const createValidatorRequest = async ( { validatorsCount: 1, id: uuid, - withdrawalAddress: eigenPodAddress, - feeRecipientAddress: nodeDelegatorAddress, - ssvOwnerAddress: nodeDelegatorAddress, + withdrawalAddress: nativeStakingStrategy, + feeRecipientAddress, + ssvOwnerAddress: nativeStakingStrategy, type: "without-encrypt-key", operationPeriodInDays: validatorSpawnOperationalPeriodInDays, } diff --git a/contracts/yarn.lock b/contracts/yarn.lock index 2ffb02816b..7b81d1aa61 100644 --- a/contracts/yarn.lock +++ b/contracts/yarn.lock @@ -1799,6 +1799,17 @@ ajv@^8.0.1: require-from-string "^2.0.2" uri-js "^4.2.2" +amazon-cognito-identity-js@^4.3.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-4.6.3.tgz#889410379a5fc5e883edc95f4ce233cc628e354c" + integrity sha512-MPVJfirbdmSGo7l4h7Kbn3ms1eJXT5Xq8ly+mCPPi8yAxaxdg7ouMUUNTqtDykoZxIdDLF/P6F3Zbg3dlGKOWg== + dependencies: + buffer "4.9.2" + crypto-js "^4.0.0" + fast-base64-decode "^1.0.0" + isomorphic-unfetch "^3.0.0" + js-cookie "^2.2.1" + amazon-cognito-identity-js@^6.0.1: version "6.2.0" resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.2.0.tgz#99e96666944429cb8f67b62e4cf7ad77fbe71ad0" @@ -2990,6 +3001,11 @@ crypto-browserify@3.12.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto-js@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + crypto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" @@ -3089,6 +3105,17 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +defender-base-client@1.38.1-rc.0: + version "1.38.1-rc.0" + resolved "https://registry.yarnpkg.com/defender-base-client/-/defender-base-client-1.38.1-rc.0.tgz#0d167845648a38bd2f5f80f5c50e081ec5f58b54" + integrity sha512-LNsuop5CE7fYyfWV3C4tlqkZawnxsMKW1SeF8TnobhZ6V5pO9LKyGSOo/V3KvPhO2/cTw5/S66+p1f6nkGCREA== + dependencies: + amazon-cognito-identity-js "^4.3.3" + async-retry "^1.3.3" + axios "^0.21.2" + lodash "^4.17.19" + node-fetch "^2.6.0" + defender-base-client@^1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/defender-base-client/-/defender-base-client-1.44.0.tgz#afe724447c0f9177b999b70b9f14dd70d61d5a7a" @@ -3100,6 +3127,17 @@ defender-base-client@^1.44.0: lodash "^4.17.19" node-fetch "^2.6.0" +defender-kvstore-client@^1.38.1-rc.0: + version "1.38.1-rc.0" + resolved "https://registry.yarnpkg.com/defender-kvstore-client/-/defender-kvstore-client-1.38.1-rc.0.tgz#6e33633a90e1355893141bad72298c6727abf30d" + integrity sha512-MeN8CtQSY72QFIys56Phxcn3tQ1nnmDXVtP/RlY9HCMynUO4zOQZaanDkgUmXTb+MYMj5VO3y+qfOfdEE+5EWA== + dependencies: + axios "^0.21.2" + defender-base-client "1.38.1-rc.0" + fs-extra "^10.0.0" + lodash "^4.17.19" + node-fetch "^2.6.0" + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -6875,9 +6913,9 @@ promise@^8.0.0: dependencies: asap "~2.0.6" -"prompts@git+https://github.com/meshin-blox/prompts.git": +"prompts@https://github.com/meshin-blox/prompts.git": version "2.4.2" - resolved "git+https://github.com/meshin-blox/prompts.git#a22bdac044f6b32ba67adb4eacc2e58322512a2d" + resolved "https://github.com/meshin-blox/prompts.git#a22bdac044f6b32ba67adb4eacc2e58322512a2d" dependencies: kleur "^4.0.1" sisteransi "^1.0.5" From 4f8b35429b8ea24eddd668db7d0c8707a553098c Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Tue, 7 May 2024 16:50:21 +0200 Subject: [PATCH 2/6] fix unit tests setup --- contracts/deploy/deployActions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/deploy/deployActions.js b/contracts/deploy/deployActions.js index 625f4c1840..4d1550cf55 100644 --- a/contracts/deploy/deployActions.js +++ b/contracts/deploy/deployActions.js @@ -585,7 +585,7 @@ const deployOETHHarvester = async (oethDripper) => { await withConfirmation( // prettier-ignore cOETHHarvesterProxy - .connect(sGovernor)["initialize(address,address,bytes)"]( + ["initialize(address,address,bytes)"]( dOETHHarvester.address, governorAddr, [] From 66f16a0df4aa3161617595d646e8d9a94fc44664 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Wed, 8 May 2024 18:06:09 +0200 Subject: [PATCH 3/6] update the registrator address of the native staking contract --- .../deploy/holesky/006_update_registrator.js | 28 +++++++++++++++++++ .../deployments/holesky/.migrations.json | 3 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 contracts/deploy/holesky/006_update_registrator.js diff --git a/contracts/deploy/holesky/006_update_registrator.js b/contracts/deploy/holesky/006_update_registrator.js new file mode 100644 index 0000000000..68f8d3b507 --- /dev/null +++ b/contracts/deploy/holesky/006_update_registrator.js @@ -0,0 +1,28 @@ +const { parseEther } = require("ethers/lib/utils"); + +const { deployNativeStakingSSVStrategy } = require("../deployActions"); +const { withConfirmation } = require("../../utils/deploy"); +const { resolveContract } = require("../../utils/resolvers"); +const addresses = require("../../utils/addresses"); + +const mainExport = async () => { + console.log("Running 006 deployment on Holesky..."); + + const cNativeStakingStrategy = await resolveContract("NativeStakingSSVStrategyProxy", "NativeStakingSSVStrategy"); + + await withConfirmation( + cNativeStakingStrategy + // Holesky defender relayer + .setRegistrator(addresses.holesky.validatorRegistrator) + ); + + console.log("Running 006 deployment done"); + return true; +}; + +mainExport.id = "006_update_registrator"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deployments/holesky/.migrations.json b/contracts/deployments/holesky/.migrations.json index 7e8f3cdbad..8448ae20e6 100644 --- a/contracts/deployments/holesky/.migrations.json +++ b/contracts/deployments/holesky/.migrations.json @@ -3,5 +3,6 @@ "002_upgrade_strategy": 1714233842, "003_deposit_to_native_strategy": 1714307581, "004_upgrade_strategy": 1714944723, - "005_deploy_new_harvester": 1714998707 + "005_deploy_new_harvester": 1714998707, + "006_update_registrator": 1715184342 } \ No newline at end of file From 559a2ed9072d89db17af21efcb4efd12530999ed Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Wed, 8 May 2024 18:35:52 +0200 Subject: [PATCH 4/6] fix up the operate validators script so that it works for native staking on holesky --- .../deploy/holesky/006_update_registrator.js | 5 +- contracts/dev.env | 4 ++ contracts/tasks/tasks.js | 69 ++++++++++++++----- contracts/tasks/validator.js | 55 ++++++++------- contracts/utils/addresses.js | 4 +- contracts/utils/signers.js | 20 +++++- contracts/utils/time.js | 7 ++ 7 files changed, 118 insertions(+), 46 deletions(-) create mode 100644 contracts/utils/time.js diff --git a/contracts/deploy/holesky/006_update_registrator.js b/contracts/deploy/holesky/006_update_registrator.js index 68f8d3b507..c14bc8dcd3 100644 --- a/contracts/deploy/holesky/006_update_registrator.js +++ b/contracts/deploy/holesky/006_update_registrator.js @@ -8,7 +8,10 @@ const addresses = require("../../utils/addresses"); const mainExport = async () => { console.log("Running 006 deployment on Holesky..."); - const cNativeStakingStrategy = await resolveContract("NativeStakingSSVStrategyProxy", "NativeStakingSSVStrategy"); + const cNativeStakingStrategy = await resolveContract( + "NativeStakingSSVStrategyProxy", + "NativeStakingSSVStrategy" + ); await withConfirmation( cNativeStakingStrategy diff --git a/contracts/dev.env b/contracts/dev.env index 874bf5275d..f7754465b6 100644 --- a/contracts/dev.env +++ b/contracts/dev.env @@ -31,3 +31,7 @@ ACCOUNTS_TO_FUND= # need of running migration scripts. # HOT_DEPLOY=strategy,vaultCore,vaultAdmin,harvester + +#P2P API KEYS +P2P_MAINNET_API_KEY=[SET API Key] +P2P_HOLESKY_API_KEY=[SET API Key] \ No newline at end of file diff --git a/contracts/tasks/tasks.js b/contracts/tasks/tasks.js index d0df225595..55fd7c0b28 100644 --- a/contracts/tasks/tasks.js +++ b/contracts/tasks/tasks.js @@ -7,7 +7,10 @@ const { smokeTest, smokeTestCheck } = require("./smokeTest"); const addresses = require("../utils/addresses"); const { getDefenderSigner } = require("../utils/signers"); const { networkMap } = require("../utils/hardhat-helpers"); +const { resolveContract } = require("../utils/resolvers"); const { KeyValueStoreClient } = require("defender-kvstore-client"); +const { operateValidators } = require("./validator"); +const { formatUnits } = require("ethers/lib/utils"); const { storeStorageLayoutForAllContracts, @@ -883,46 +886,78 @@ task("depositSSV").setAction(async (_, __, runSuper) => { }); // Defender -subtask("operateValidators", "Creates the required amount of new SSV validators and stakes ETH") +subtask( + "operateValidators", + "Creates the required amount of new SSV validators and stakes ETH" +) .addOptionalParam("index", "Index of Native Staking contract", 1, types.int) - .addOptionalParam("stake", "Stake 32 ether after registering a new SSV validator", true, types.boolean) - .addOptionalParam("days", "SSV Cluster operational time in days", 1, types.int) + .addOptionalParam( + "stake", + "Stake 32 ether after registering a new SSV validator", + true, + types.boolean + ) + .addOptionalParam( + "days", + "SSV Cluster operational time in days", + 40, + types.int + ) .addOptionalParam("clear", "Clear storage", true, types.boolean) .setAction(async (taskArgs) => { - const storeFilePath = require("path").join(__dirname, "..", ".localKeyValueStorage"); const network = await ethers.provider.getNetwork(); const isMainnet = network.chainId === 1; const isHolesky = network.chainId === 17000; + const addressesSet = isMainnet ? addresses.mainnet : addresses.holesky; if (!isMainnet && !isHolesky) { - throw new Error("operate validatos is supported on Mainnet and Holesky only"); + throw new Error( + "operate validatos is supported on Mainnet and Holesky only" + ); } - // TODO separate store file for a separate network + const storeFilePath = require("path").join( + __dirname, + "..", + `.localKeyValueStorage${isMainnet ? "Mainnet" : "Holesky"}` + ); + const store = new KeyValueStoreClient({ path: storeFilePath }); const signer = await getDefenderSigner(); - const WETH = await ethers.getContractAt("IWETH9", addresses.mainnet/holesky.WETH); + const WETH = await ethers.getContractAt("IWETH9", addressesSet.WETH); + const SSV = await ethers.getContractAt("IERC20", addressesSet.SSV); + + // TODO: use index to target different native staking strategies when we have more than 1 + const nativeStakingStrategy = await resolveContract( + "NativeStakingSSVStrategyProxy", + "NativeStakingSSVStrategy" + ); - const addressName = taskArgs.index === 1 ? "NODE_DELEGATOR_NATIVE_STAKING" : "NODE_DELEGATOR"; - const nodeDelegatorAddress = await parseAddress(addressName); - const nodeDelegator = await ethers.getContractAt("NodeDelegator", nodeDelegatorAddress); + log( + "Balance of SSV tokens on the native staking contract: ", + formatUnits(await SSV.balanceOf(nativeStakingStrategy.address)) + ); const contracts = { - nodeDelegator, + nativeStakingStrategy, WETH, }; + const feeAccumulatorAddress = + await nativeStakingStrategy.FEE_ACCUMULATOR_ADDRESS(); - const eigenPodAddress = await parseAddress("EIGEN_POD"); - - const p2p_api_key = network.chainId === 1 ? process.env.P2P_MAINNET_API_KEY : process.env.P2P_HOLESKY_API_KEY; + const p2p_api_key = isMainnet + ? process.env.P2P_MAINNET_API_KEY + : process.env.P2P_HOLESKY_API_KEY; if (!p2p_api_key) { - throw new Error("P2P API key environment variable is not set. P2P_MAINNET_API_KEY or P2P_HOLESKY_API_KEY"); + throw new Error( + "P2P API key environment variable is not set. P2P_MAINNET_API_KEY or P2P_HOLESKY_API_KEY" + ); } - const p2p_base_url = network.chainId === 1 ? "api.p2p.org" : "api-test-holesky.p2p.org"; + const p2p_base_url = isMainnet ? "api.p2p.org" : "api-test-holesky.p2p.org"; const config = { - eigenPodAddress, + feeAccumulatorAddress, p2p_api_key, p2p_base_url, // how much SSV (expressed in days of runway) gets deposited into the diff --git a/contracts/tasks/validator.js b/contracts/tasks/validator.js index 5bd5759be2..66b3530a27 100644 --- a/contracts/tasks/validator.js +++ b/contracts/tasks/validator.js @@ -5,8 +5,8 @@ const { v4: uuidv4 } = require("uuid"); const { resolveContract } = require("../utils/resolvers"); const { getSigner } = require("../utils/signers"); -const { getClusterInfo } = require("../utils/ssv"); const { sleep } = require("../utils/time"); +const { getClusterInfo } = require("./ssv"); const { logTxDetails } = require("../utils/txLogger"); const log = require("../utils/logger")("task:p2p"); @@ -36,12 +36,12 @@ const ERROR_THRESHOLD = 5; */ const operateValidators = async ({ store, signer, contracts, config }) => { const { - clear, - eigenPodAddress, + feeAccumulatorAddress, p2p_api_key, - validatorSpawnOperationalPeriodInDays, p2p_base_url, + validatorSpawnOperationalPeriodInDays, stake, + clear, } = config; let currentState = await getState(store); @@ -52,8 +52,8 @@ const operateValidators = async ({ store, signer, contracts, config }) => { currentState = undefined; } - if (!(await nodeDelegatorHas32Eth(contracts))) { - log(`Node delegator doesn't have enough ETH, exiting`); + if (!(await stakingContractHas32ETH(contracts))) { + log(`Native staking contract doesn't have enough ETH, exiting`); return; } @@ -63,8 +63,8 @@ const operateValidators = async ({ store, signer, contracts, config }) => { await createValidatorRequest( p2p_api_key, // api key p2p_base_url, - nativeStakingStrategy.address, // node delegator address - feeRecipientAddress, // eigenPod address + contracts.nativeStakingStrategy.address, // SSV owner address & withdrawal address + feeAccumulatorAddress, // execution layer fee recipient validatorSpawnOperationalPeriodInDays, store ); @@ -87,7 +87,7 @@ const operateValidators = async ({ store, signer, contracts, config }) => { store, currentState.uuid, currentState.metadata, - contracts.nodeDelegator + contracts.nativeStakingStrategy ); currentState = await getState(store); } @@ -96,7 +96,7 @@ const operateValidators = async ({ store, signer, contracts, config }) => { await waitForTransactionAndUpdateStateOnSuccess( store, currentState.uuid, - contracts.nodeDelegator.provider, + contracts.nativeStakingStrategy.provider, currentState.metadata.validatorRegistrationTx, "registerSsvValidator", // name of transaction we are waiting for "validator_registered" // new state when transaction confirmed @@ -111,7 +111,7 @@ const operateValidators = async ({ store, signer, contracts, config }) => { signer, store, currentState.uuid, - contracts.nodeDelegator, + contracts.nativeStakingStrategy, currentState.metadata.depositData ); currentState = await getState(store); @@ -121,7 +121,7 @@ const operateValidators = async ({ store, signer, contracts, config }) => { await waitForTransactionAndUpdateStateOnSuccess( store, currentState.uuid, - contracts.nodeDelegator.provider, + contracts.nativeStakingStrategy.provider, currentState.metadata.depositTx, "stakeEth", // name of transaction we are waiting for "deposit_confirmed" // new state when transaction confirmed @@ -251,14 +251,14 @@ const getState = async (store) => { return JSON.parse(await store.get("currentRequest")); }; -const nodeDelegatorHas32Eth = async (contracts) => { - const address = contracts.nodeDelegator.address; +const stakingContractHas32ETH = async (contracts) => { + const address = contracts.nativeStakingStrategy.address; const wethBalance = await contracts.WETH.balanceOf(address); - const ethBalance = await contracts.nodeDelegator.provider.getBalance(address); - const totalBalance = wethBalance.add(ethBalance); - log(`Node delegator has ${formatUnits(totalBalance, 18)} ETH in total`); - return totalBalance.gte(parseEther("32")); + log( + `Native staking contract has ${formatUnits(wethBalance, 18)} WETH in total` + ); + return wethBalance.gte(parseEther("32")); }; /* Make a GET or POST request to P2P service @@ -305,7 +305,7 @@ const createValidatorRequest = async ( p2p_api_key, p2p_base_url, nativeStakingStrategy, - feeRecipientAddress, + feeAccumulatorAddress, validatorSpawnOperationalPeriodInDays, store ) => { @@ -318,8 +318,9 @@ const createValidatorRequest = async ( validatorsCount: 1, id: uuid, withdrawalAddress: nativeStakingStrategy, - feeRecipientAddress, + feeRecipientAddress: feeAccumulatorAddress, ssvOwnerAddress: nativeStakingStrategy, + // TODO: we need to alter this and store the key somewhere type: "without-encrypt-key", operationPeriodInDays: validatorSpawnOperationalPeriodInDays, } @@ -348,14 +349,20 @@ const waitForTransactionAndUpdateStateOnSuccess = async ( await updateState(uuid, newState, store); }; -const depositEth = async (signer, store, uuid, nodeDelegator, depositData) => { +const depositEth = async ( + signer, + store, + uuid, + nativeStakingStrategy, + depositData +) => { const { pubkey, signature, depositDataRoot } = depositData; try { log(`About to stake ETH with:`); log(`pubkey: ${pubkey}`); log(`signature: ${signature}`); log(`depositDataRoot: ${depositDataRoot}`); - const tx = await nodeDelegator.connect(signer).stakeEth([ + const tx = await nativeStakingStrategy.connect(signer).stakeEth([ { pubkey, signature, @@ -380,7 +387,7 @@ const broadcastRegisterValidator = async ( store, uuid, metadata, - nodeDelegator + nativeStakingStrategy ) => { const registerTransactionParams = defaultAbiCoder.decode( [ @@ -414,7 +421,7 @@ const broadcastRegisterValidator = async ( log(`cluster: ${cluster}`); try { - const tx = await nodeDelegator + const tx = await nativeStakingStrategy .connect(signer) .registerSsvValidator( publicKey, diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index aa6885e32e..4d2ac56496 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -271,6 +271,8 @@ addresses.holesky.beaconChainDepositContract = "0x4242424242424242424242424242424242424242"; addresses.holesky.OETHVaultProxy = "0x19d2bAaBA949eFfa163bFB9efB53ed8701aA5dD9"; +// Address of the Holesky defender relayer addresses.holesky.validatorRegistrator = - "0x1b94CA50D3Ad9f8368851F8526132272d1a5028C"; + "0x3C6B0c7835a2E2E0A45889F64DcE4ee14c1D5CB4"; + module.exports = addresses; diff --git a/contracts/utils/signers.js b/contracts/utils/signers.js index 3ec192d55c..439d6ee07f 100644 --- a/contracts/utils/signers.js +++ b/contracts/utils/signers.js @@ -66,16 +66,29 @@ const getDefenderSigner = async () => { ); process.exit(2); } + + const network = await ethers.provider.getNetwork(); + const isMainnet = network.chainId === 1; + const isHolesky = network.chainId === 17000; + + const apiKeyName = isMainnet + ? "DEFENDER_API_KEY" + : "HOLESKY_DEFENDER_API_KEY"; + const apiKeySecret = isMainnet + ? "DEFENDER_API_SECRET" + : "HOLESKY_DEFENDER_API_SECRET"; + const credentials = { - relayerApiKey: process.env.DEFENDER_API_KEY, - relayerApiSecret: process.env.DEFENDER_API_SECRET, + relayerApiKey: process.env[apiKeyName], + relayerApiSecret: process.env[apiKeySecret], }; + const client = new Defender(credentials); const provider = client.relaySigner.getProvider(); const signer = client.relaySigner.getSigner(provider, { speed }); log( - `Using Defender Relayer account ${await signer.getAddress()} from env vars DEFENDER_API_KEY and DEFENDER_API_SECRET` + `Using Defender Relayer account ${await signer.getAddress()} from env vars ${apiKeyName} and ${apiKeySecret}` ); return signer; }; @@ -112,4 +125,5 @@ module.exports = { getSigner, impersonateAccount, impersonateAndFund, + getDefenderSigner, }; diff --git a/contracts/utils/time.js b/contracts/utils/time.js new file mode 100644 index 0000000000..d761801638 --- /dev/null +++ b/contracts/utils/time.js @@ -0,0 +1,7 @@ +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +module.exports = { + sleep, +}; From f59cd6bd8698c45c4e8815617714c5a0dd8fa526 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Wed, 8 May 2024 18:36:55 +0200 Subject: [PATCH 5/6] also add the gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f4a57a0d11..7770a7cb26 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,8 @@ contracts/coverage.json contracts/build/ contracts/dist/ contracts/.localKeyValueStorage +contracts/.localKeyValueStorageMainnet +contracts/.localKeyValueStorageHolesky todo.txt brownie/env-brownie/ From 32489fb25da3a589c0f7614b3c43877035568fd3 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Wed, 8 May 2024 23:16:21 +0200 Subject: [PATCH 6/6] add ability to exit if staking contract is paused --- contracts/tasks/validator.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contracts/tasks/validator.js b/contracts/tasks/validator.js index 66b3530a27..9b9f484238 100644 --- a/contracts/tasks/validator.js +++ b/contracts/tasks/validator.js @@ -57,6 +57,11 @@ const operateValidators = async ({ store, signer, contracts, config }) => { return; } + if (await stakingContractPaused(contracts)) { + log(`Native staking contract is paused... exiting`); + return; + } + const executeOperateLoop = async () => { while (true) { if (!currentState) { @@ -251,6 +256,13 @@ const getState = async (store) => { return JSON.parse(await store.get("currentRequest")); }; +const stakingContractPaused = async (contracts) => { + const paused = await contracts.nativeStakingStrategy.paused(); + + log(`Native staking contract is ${paused ? "" : "not "}paused`); + return paused; +}; + const stakingContractHas32ETH = async (contracts) => { const address = contracts.nativeStakingStrategy.address; const wethBalance = await contracts.WETH.balanceOf(address);