fix: security and correctness follow-ups for Sepolia operator tooling (PR #176)#177
Merged
lionakhnazarov merged 5 commits intofeat/testnet4-deployment-supportfrom Apr 15, 2026
Conversation
- Remove private key logging from create-operator-keystore and get-operator-key; address is sufficient for confirmation - Remove staking provider and operator private keys from generated .env files; keys written to disk are a git-leak risk - Print staking provider key once to terminal with a prominent "copy now" warning instead of persisting it to .env - Require non-empty password in create-operator-keystore and setup-new-staking-provider; empty-password keystores are trivially decryptable - Require explicit keystore path in get-operator-key; remove the hardcoded developer-machine UUID default that caused ENOENT for all other users - Fix --list path in get-operator-key: ../../operator-1-keystore resolved above repo root; corrected to ../operator-1-keystore - Convert sync fs calls to fs.promises and add try/catch inside main() across all three scripts
…args Passing --private-key as a CLI argument exposes the key in ps aux output and persists in shell history. Using ETH_PRIVATE_KEY as an inline env assignment (ETH_PRIVATE_KEY="$key" cast send ...) keeps the key out of the argv list. Introduce _sp_cast_send_ok / _op_cast_send_ok wrappers in run-new-operator-setup.sh that inject ETH_PRIVATE_KEY for the respective signer, and replace all --private-key flag usages. Update fund-new-operator.sh likewise for the deployer key. Update run-new-operator-setup.sh usage comment to reflect that NEW_STAKING_PROVIDER_KEY and NEW_OPERATOR_KEY are no longer written to .env files and must be exported by the operator.
- Drop kind: "transparent" from upgradeProxy options; let the OZ
plugin infer the proxy type from the deployed proxy admin slot.
Hardcoding the kind risks a mismatch if the original deploy
defaulted differently. Add a comment with the cast storage
command to verify proxy type on-chain.
- Replace two inline const fs = require("fs") declarations with a
single top-level import * as fs from "fs" to match TypeScript
conventions and avoid the duplicate binding.
The file was removed in the parent branch commit without explanation. Downstream consumers relying on deployments/mainnet/TokenStaking.json break silently without it. Restored from the last known-good version (commit ab29e02).
approveApplication already checks application != address(0) but increaseAuthorization did not. The APPROVED status check provides a functional backstop, but adding the explicit guard makes the invariant consistent across both entry points.
lionakhnazarov
approved these changes
Apr 15, 2026
1e32bf1
into
feat/testnet4-deployment-support
11 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Follow-up to #176 (Sepolia TokenStaking deploy, ExtendedTokenStaking upgrade, and operator setup tooling). Addresses security and correctness issues identified in code review.
Changes
P0 -- Private key exposure
scripts/create-operator-keystore.js-- removeconsole.log("Private key:", ...). Address and keystore path are sufficient output.scripts/get-operator-key.js-- same; remove private key log line.scripts/setup-new-staking-provider.js-- removeNEW_STAKING_PROVIDER_KEYandNEW_OPERATOR_KEYfrom generated.envfiles (keys written to disk are agit add .leak risk). Staking provider key is still printed once to terminal with a "copy now" warning since it is generated ephemerally.P1 -- Keys in CLI arguments
scripts/run-new-operator-setup.sh-- replace all--private-key $KEYflags withETH_PRIVATE_KEY="$KEY" cast sendvia_sp_cast_send_ok/_op_cast_send_okwrappers.--private-keyexposes the raw key inps auxand shell history.scripts/fund-new-operator.sh-- same for the deployer key.P1 -- Operator key script bugs
scripts/get-operator-key.js-- remove hardcoded UUID default path (c88df450-...) that causedENOENTfor all users except the original developer. Require explicit path argument with usage error.scripts/get-operator-key.js-- fix--listdirectory path:../../operator-1-keystoreresolved above the repo root; corrected to../operator-1-keystore.P1 -- Proxy upgrade kind
deploy/54_upgrade_token_staking_extended.ts-- remove hardcodedkind: "transparent"fromupgradeProxyoptions. Let the OZ plugin infer the proxy type. Hardcoding risks a mismatch if the plugin defaulted differently during the original deploy. Add a comment with thecast storagecommand to verify the proxy type on-chain before any future mainnet run.P2 -- Code quality and correctness
scripts/create-operator-keystore.jsandscripts/setup-new-staking-provider.js-- require non-empty password; empty-password keystores are trivially decryptable.deploy/54_upgrade_token_staking_extended.ts-- replace two inlineconst fs = require("fs")with a single top-levelimport * as fs from "fs".deployments/mainnet/TokenStaking.json-- restore file deleted without explanation in Sepolia TokenStaking proxy deploy, ExtendedTokenStaking upgrade, and operator setup tooling #176. Downstream consumers relying ondeployments/mainnet/break silently.contracts/staking/TokenStaking.sol-- addapplication != address(0)guard inincreaseAuthorizationfor consistency withapproveApplication.Housekeeping (async refactor in scripts)
The three JS scripts were also converted from sync
fs.*Synccalls tofs.promises.*and gainedtry/catchinsidemain(). This was required to pass the pre-commit UBS scan (sync I/O in scripts triggers a warning) but also makes error handling explicit.Test plan
node scripts/create-operator-keystore.jswithout args exits with usage errornode scripts/create-operator-keystore.js mypasswordcreates keystore, prints address only (no key)node scripts/get-operator-key.jswithout args exits with usage errornode scripts/get-operator-key.js --listlists keystores fromoperator-1-keystore/(not parent dir)node scripts/setup-new-staking-provider.jswithout args exits with usage errornode scripts/setup-new-staking-provider.js mypasswordgenerates wallets;.env.new-operatorcontains addresses but no private keys; terminal shows SP key with warningdeployments/mainnet/TokenStaking.jsonexists and has correct address0x01B67b1194C75264d06F808A921228a95C765dd7yarn compilepasses