Skip to content

Support safe Sepolia staking upgrades and operator setup - #176

Open
lionakhnazarov wants to merge 44 commits into
mainfrom
feat/testnet4-deployment-support
Open

lionakhnazarov wants to merge 44 commits into
mainfrom
feat/testnet4-deployment-support

Conversation

@lionakhnazarov

@lionakhnazarov lionakhnazarov commented Apr 7, 2026

Copy link
Copy Markdown

Sepolia operators need native T staking and authorization for Random Beacon and Wallet Registry. This adds that workflow through a dedicated SepoliaTokenStaking implementation, with proxy deployment, upgrade, funding, keystore, and operator registration tooling.

  • Both upgrade entrypoints select the guarded Sepolia implementation. It exposes native staking without test-fixture mutators, emits native-T Staked events for indexers, keeps every approved application's authorization backed during decreases and withdrawals, and disables legacy TACo migration on Sepolia. The base contract retains TACo eligibility and rejects deprecated applications before changing authorization.
  • Mainnet and Sepolia deployment reruns reuse their recorded proxy. A record pointing to missing code stops deployment for reconciliation. Direct local deployments retain hardhat-deploy's normal artifact/constructor-change handling.
  • Sepolia deployment and upgrade resolve artifacts through hardhat-deploy, including external.contracts. The package includes matching compiler build information, which the consuming project's OpenZeppelin plugin validates in its own cache format before deploying. Missing or mismatched build information stops the operation before a transaction; existing consumer validation entries remain intact.
  • The shipped Sepolia OpenZeppelin manifest now matches the recorded proxy, its current admin, and the historical ExtendedTokenStaking layout, including skipList. Its implementation bytecode was rebuilt from the deployment commit and matched against Sepolia. The manifest is included in npm distributions; upgrade-history documentation explains provenance and consumer/historical-admin requirements.
  • Upgrades preserve deployment metadata and save the implementation/ABI through deployments.save, so later operations and exports include stake().
  • Credential generation creates private files exclusively, preserves existing wallets on retries, and allows a fresh attempt after preliminary generation fails. Both singular .env.operator-* and plural .env.operators-* credential files are ignored.
  • Funding rejects missing, empty, or unreadable explicit configuration paths before transferring tokens; the default file is used only when no argument is supplied. All operator setup commands load shared, selected, and generated configuration from explicit filesystem paths, preventing same-named files on PATH from selecting another provider while preserving each command's working-directory behavior.
  • setup-multiple-operators.sh uses the same normalized decimal wei amount for the ETH balance check and both wallet transfers. Unitless ETH_PER_OPERATOR values are interpreted as ether, so 0.05 and 1 fund each wallet with the intended amount.
  • Operator setup propagates each failed transaction and reports incomplete registrations with a nonzero exit status. Ambiguous broadcast or receipt failures stop without rebuilding a potentially non-idempotent transaction; operators must reconcile the original submission before retrying.

Validation on the remediation commits:

  • npm run prepack (including Solidity compilation, TypeScript and artifact/build-info export) and tsc -p tsconfig.export.json --noEmit passed.
  • Full Hardhat test suite: 443 passing, with gas reporting disabled locally. This includes 63 added checks covering contract authorization/backing, staking events, storage-compatible upgrades from the committed Sepolia history, proxy reuse, deployment exports, external artifact consumption, key preservation, configuration selection across operator entrypoints, exact ETH funding amounts, failure propagation, and single-attempt transaction submission.
  • A real npm tarball was tested in consumers without locally compiled staking artifacts. Deployment, upgrade, missing-metadata and mismatched-artifact checks passed using both this repository's dependency tree and tBTC's installed Hardhat 2.12.5 / OpenZeppelin plugin 1.22.0 dependency tree.
  • hardhat deploy --network hardhat --export /tmp/pr176-followup-export.json passed.
  • The standalone upgrade command rejected the local Hardhat network before deployment, as expected.
  • Repository-pinned Slither 0.8.0: 0 findings; modified-file ESLint, Solhint, Prettier, credential-ignore and diff checks passed. Bash syntax checks passed in the initial remediation.

The broader npm run format command still reports 58 pre-existing style errors in three unchanged keystore scripts; the changed JavaScript and shell files pass the applicable lint, formatting, and syntax checks.

No public-network deployment was performed. Coordinate the shared deployment-script changes with #186 and the toolchain refresh in #182 when integrating these PRs.

…rease in TokenStaking

- Introduced  method in  and  to allow governance to approve applications.
- Added  method to enable staking providers to increase their authorization for applications.
- Updated deployment scripts for TokenStaking upgrade and added new scripts for operator keystore management.
- Modified  to include a new script for upgrading token staking.
- Created new JSON file for TokenStaking ABI and removed outdated deployment data.

@lrsaturnino lrsaturnino left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two critical findings from multi-model code review (3/3 models confirmed both).

Comment thread contracts/test/TokenStakingTestSet.sol Outdated
Comment thread package.json Outdated
lionakhnazarov and others added 11 commits April 13, 2026 16:02
- Updated the approveApplication function to include the onlyGovernance modifier, ensuring that only governance can approve applications.
… for Yarn

- Upgraded actions/setup-node from v3 to v4 across multiple workflow files.
- Added a step to enable Corepack for Yarn in the workflows, ensuring compatibility with the package manager specified in the project.
…ld-network/solidity-contracts into feat/testnet4-deployment-support
- Removed comments from .yarnrc.yml to streamline configuration.
- Updated packageManager in package.json to Yarn version 4.12.0 for improved performance and features.
…docs file

- Changed the workflow to use the local path for reusable-solidity-docs.yml instead of the remote repository reference.
- Ensured that both documentation generation and publishing jobs point to the correct local file.
- 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 and others added 2 commits April 15, 2026 12:59
…and-correctness

fix: security and correctness follow-ups for Sepolia operator tooling (PR #176)
lrsaturnino
lrsaturnino previously approved these changes Apr 17, 2026

@lrsaturnino lrsaturnino left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall — clean contract changes, CEI ordering solid, access controls in place.

One small nit: the operator setup scripts generate keystores and env files (operator-1-keystore/, spv-maintainer-keystore/, .env.new-operator, .env.operator-*) that aren't covered by .gitignore. Might be worth adding entries to prevent accidental commits.

@lrsaturnino lrsaturnino left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more findings for evaluation.

Comment thread deploy/07_deploy_token_staking.ts Outdated
Comment thread deploy/54_upgrade_token_staking_extended.ts Outdated
Comment thread deploy/54_upgrade_token_staking_extended.ts Outdated
Comment thread scripts/upgrade-token-staking.ts Outdated
Comment thread scripts/setup-multiple-operators.sh Outdated
- Added new entries to .gitignore for generated operator setup artifacts.
- Updated deployment scripts to create a directory for network-specific deployments and save TokenStaking deployment data in both the root and network-specific directories.
- Refactored authorization and registration commands in setup scripts to use environment variables for private keys, improving security and readability.
- Modified upgrade script documentation to reflect the correct command usage from the repository root.
…ld-network/solidity-contracts into feat/testnet4-deployment-support

@lrsaturnino lrsaturnino left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On ExtendedTokenStaking in contracts/test/TokenStakingTestSet.sol — now that approveApplication and increaseAuthorization live on the base TokenStaking, the overrides at lines 227–268 and 280–294 are duplicates. The increaseAuthorization override also doesn't emit AuthorizationIncreased or guard application != address(0), while the base does both.

Since deploy/54 makes ExtendedTokenStaking the live Sepolia implementation, Sepolia will silently stop emitting AuthorizationIncreased (no test asserts it, so CI won't flag it). Dropping both overrides should be enough — the derived contract picks up the canonical behaviour from the base.

- Introduced a  function to remove CRLF and whitespace from environment variables, preventing decoding errors.
- Updated  to ensure that the  is not overwritten by stale values.
- Modified  to store both staking provider and operator private keys in the environment file for automated setups, while ensuring sensitive information is not logged unnecessarily.
- Added error handling for missing keys in the generated environment files.
- Added validation to ensure ETH_PRIVATE_KEY is set before sending transactions.
- Introduced a mechanism to prevent overwriting the deployer key with stale values from environment files.
- Updated the script to maintain the correct private key for the contract owner during operator setup.
- Updated prerequisites for deploying operators to include AUTO_FUND_T for automatic minting of T tokens.
- Added a function to compute T token shortfall and validate the deployer's balance.
- Implemented error handling for insufficient T balance and ensured proper private key management for minting.
- Introduced normalization for addresses to improve consistency in key comparisons.
--list was calling fromEncryptedJson with an empty password to extract
the address. Keystore JSON contains an unencrypted "address" field;
reading it directly avoids spurious decryption errors for keystores
with real passwords and removes the dependency on the ethers decrypt path.
- Remove hardcoded fallback T token address; fail loudly with a clear
  error if T.json is missing rather than silently using a stale address
- Quote all shell variables in the cast send call to prevent word
  splitting on RPC URLs that include API key query parameters
07_deploy_token_staking.ts was writing to both the root TokenStaking.json
and deployments/<network>/TokenStaking.json. After the upgrade script runs,
only the network-specific file is updated, leaving the root file with a
stale ABI. Write only to deployments/<network>/ and delete the root-level
artifact that was previously committed.

Also fix pre-existing issues in the file: use strict equality, static
import for fs, const for jsonAbi, async fs.promises, and try/catch around
JSON.parse.
lionakhnazarov and others added 4 commits May 5, 2026 17:41
…s.sh to use positional arguments for cast wallet address

- Refactored fund-new-operator.sh to utilize cast_send_ok for sending tokens, improving error handling.
- Modified setup-multiple-operators.sh to pass private keys as positional arguments to cast wallet address, addressing security concerns with environment variable exposure.
cast send and cast wallet address do not honor ETH_PRIVATE_KEY as an
environment variable, so passing the key via --private-key (or as a
positional argument to cast wallet address) leaves it visible in
/proc/<pid>/cmdline and `ps auxww` output for every subprocess.

Move signing through a v3 keystore + password file:

- scripts/lib/wallet.js: read ETH_PRIVATE_KEY from env (never argv),
  derive the address, or write an encrypted v3 keystore. Foundry
  expects lower-case "crypto"; ethers v5 emits "Crypto", so the field
  name is rewritten before persisting.
- scripts/lib/cast-helpers.sh: lazy-create a per-process keystore
  directory (mode 700, in $TMPDIR), encrypt each unique key on first
  use, cache by derived address, remove the directory via an EXIT trap
  that chains onto any existing trap. cast_send_ok now calls cast send
  with --keystore + --password-file. derive_address_safe replaces the
  `cast wallet address <KEY>` callers.
- scripts/setup-multiple-operators.sh: switch the three cast wallet
  address callsites to derive_address_safe.

Threat model is process argv exposure on multi-tenant runners; the
keystore lives only for the lifetime of the sourcing shell and is
never persisted across invocations.
The previous commit moved signing through a v3 keystore so the key
stopped appearing in argv (visible to all users via `ps auxww`). The
temporary env assignment used by callers,

    ETH_PRIVATE_KEY="$KEY" cast_send_ok ...

is still propagated into every child process spawned inside
cast_send_ok, including the `cast send` invocation, so the key remained
readable from /proc/<cast-pid>/environ for the lifetime of the
subprocess (same-user/root only on Linux, but nonzero).

cast send and cast receipt sign / inspect via --keystore + --password-file
at this point and do not need the variable. Wrap both invocations with
`env -u ETH_PRIVATE_KEY` so the variable is dropped from their
environment. The wallet.js subprocesses still inherit it because they
read ETH_PRIVATE_KEY from env to derive the address and to encrypt the
keystore.
@lrsaturnino

Copy link
Copy Markdown
Member

.openzeppelin/unknown-11155111.json (the OZ Sepolia manifest) doesn't seem to be tracked, and .gitignore:21 matches the .openzeppelin/unknown-*.json pattern that covers it. That could leave a fresh clone tripping on Manifest not found the next time someone runs the Sepolia upgrade, and the storage-layout history wouldn't carry forward across upgrades.

Probably a quick follow-up — commit the manifest (from whoever ran deployProxy originally) plus a !.openzeppelin/unknown-11155111.json carve-out after .gitignore:21, mirroring how mainnet.json is already tracked.

lionakhnazarov and others added 2 commits May 11, 2026 19:41
Add a .gitignore exception for unknown-11155111.json so the OpenZeppelin
manifest is versioned like mainnet layout history, avoiding "Manifest not
found" on fresh clones when running Sepolia upgrades.

Co-authored-by: Cursor <cursoragent@cursor.com>
…llowup

chore: track OpenZeppelin Sepolia upgrade manifest
@mswilkison mswilkison changed the title Sepolia TokenStaking proxy deploy, ExtendedTokenStaking upgrade, and operator setup tooling Support safe Sepolia staking upgrades and operator setup Sep 8, 2026
@mswilkison

mswilkison commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Addressed all seven review findings in 0fab89a:

Review finding Remediation and regression evidence
Public deployment selected a test fixture Both upgrade entrypoints share the Sepolia-only upgrade function and select SepoliaTokenStaking; ABI and authorization tests exclude test mutators and enforce governance/authorizer controls.
Reruns replaced the Sepolia proxy Existing mainnet/Sepolia proxy records are reused, missing code fails before replacement, and tests verify address/state preservation. Local direct deployment refresh is preserved.
Deprecated application authorizations were inconsistent The base entrypoint applies its eligibility guard. Sepolia supports the complete approved-application lifecycle, including multi-application withdrawal backing and disabled legacy migration.
Setup overwrote staking-provider keys Exclusive private env/keystore creation preserves existing credentials. Tests compare original file hashes across retries, reject symlinks, and cover preliminary directory failures.
Existing-operator setup hid transaction failures Every transaction explicitly returns failure; partial registrations make the script exit nonzero. Each of the six transaction positions is covered.
Upgraded ABI was missing from exports deployments.save updates the registry and disk together, preserving metadata. Tests confirm exported stake().
Ambiguous broadcasts could duplicate transfers Submission and receipt errors stop without reconstructing the send. Mocked cases confirm exactly one send for already-known, nonce, missing-hash, and receipt failures.

Validation: 410 tests passed (30 new), compilation, packaging, local deployment/export, repository-pinned Slither 0.8.0 (0 findings), modified-file lint/format, and Bash syntax. Storage upgrade checks cover both the base contract and previous fixture layout.

Independent candidate review also identified local redeployment and failed-generation retry regressions; both are corrected and included in the tests. These are source fixes; no Sepolia or mainnet deployment was performed.

GitHub CI on 0fab89a is green: build/tests, deployment dry run, Slither, formatting workflow, and documentation preview all passed.

@mswilkison

Copy link
Copy Markdown
Contributor

Fixed the three follow-up findings in a8fe18c.

  1. External artifacts and upgrade validation: Both Sepolia entrypoints resolve factories through hardhat-deploy's artifact registry. The package now includes matching compiler build information, and the consuming project's OpenZeppelin plugin regenerates its own validation entries. Existing entries remain intact, and the normal deployment/storage-layout checks remain enabled. Missing metadata or a bytecode/ABI mismatch is rejected before a transaction.
  2. Staking events: SepoliaTokenStaking.stake emits the inherited native-T Staked event with the owner, provider, beneficiary, authorizer and amount. The regression test checks every field, exactly one event, the stake balance, and the matching withdrawal.
  3. Credential filenames: .gitignore now covers .env.operators-*, including the documented .env.operators-3 file and nested equivalents.

Changed files: deploy/07_deploy_token_staking.ts, deploy/54_upgrade_token_staking_extended.ts, scripts/staking-artifacts.ts, scripts/export-staking-build-info.js, package.json, contracts/staking/SepoliaTokenStaking.sol, .gitignore, test/deployment/ExternalStakingDeployment.test.js, and test/staking/SepoliaTokenStaking.test.js.

Verification, in order:

  • Build/type checks: npm run prepack and tsc -p tsconfig.export.json --noEmit passed.
  • Focused regression and compatibility checks: an actual npm tarball deploys and upgrades in an external.contracts consumer with no locally compiled staking artifacts. The proxy address, stored configuration, deployment metadata, exported ABI and pre-existing validation entries are preserved. Missing-metadata and mismatched-artifact cases reject before sending a transaction. These checks passed with this repository's Hardhat 2.10.2 / OpenZeppelin plugin 1.14.0 dependencies and tBTC's installed Hardhat 2.12.5 / plugin 1.22.0 dependencies.
  • Full suite: 414 passing, using matched compiled artifacts and gas reporting disabled locally:
    const hre = require("hardhat")
    hre.config.gasReporter = { enabled: false }
    await hre.run("test", { noCompile: true })
    Run through Node 18 with ts-node/register; test failures propagate as a nonzero exit status.
  • Local deployment: hardhat deploy --network hardhat --export /tmp/pr176-followup-export.json passed.
  • Static checks: repository-pinned slither . --ignore-compile (0.8.0) reported 0 findings. Modified-file ESLint, Solhint, Prettier, credential-ignore checks and git diff --check passed.
  • An independent read-only review found no surviving bypasses or regressions in this patch.

No public-network deployment or live subgraph validation was performed. The downstream checks use tBTC's dependency versions in isolated local consumers; they do not deploy the entire tBTC system. GitHub CI results will be recorded separately.

@mswilkison

Copy link
Copy Markdown
Contributor

Fixed the CI installation failure in a7ba9ac.

The failed run stopped while installing dependencies: concurrent Yarn Classic bootstraps for Git dependencies shared the runner's cache and hit an ENOENT for lodash/fp/valuesIn.js.

The Solidity, format, npm and reusable docs workflows now pass YARN_CACHE_FOLDER=.yarn/cache. Yarn Classic resolves that relative path inside each dependency's temporary checkout, so parallel bootstraps have separate caches. Yarn 4 retains its existing global cache behavior. Dependencies and the lockfile are unchanged.

Verified that two concurrent Yarn Classic 1.22.22 processes resolve different cache directories, and that all four workflows parse correctly. All checks passed on this commit: Solidity (build/tests, deployment dry run and Slither), format, and documentation preview.

@mswilkison

mswilkison commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Addressed both new findings in 3620f4d.

  • Restored the recorded Sepolia proxy's matching OpenZeppelin implementation history and ProxyAdmin. The complete historical layout includes skipList; all 45 previous implementation entries and 33 previous proxy records remain intact. The manifest is now included in the npm package.
  • Funding now rejects a missing, empty, directory, or unreadable explicit configuration path before transferring tokens. Defaults apply only when no argument is supplied. Explicit and default loads are anchored to the checked filesystem paths, so a same-named file on PATH cannot select a different provider.

The manifest recovery is based on read-only Sepolia evidence at block 11663843. Recompiling historical commit ad7c06e23672ca7b787f6bb882d519e0e5851dc3 with Solidity 0.8.9+commit.e5eed63a matches the deployed implementation byte-for-byte, including metadata and all seven immutable token references. A separate reviewer independently reproduced the compiler/layout match and corroborated the proxy, implementation, and admin through public RPC. See the provenance and upgrade instructions.

Verification:

  • Syntax and static checks: bash -n scripts/fund-new-operator.sh, ESLint for the three changed JavaScript test files, Prettier for the changed files covered by repository configuration, and git diff --check passed.
  • Regression checks: before the patch, upgrading the recorded snapshot failed with “Deployment at address ... is not registered,” and invalid explicit funding paths selected the default wallet. The three recorded-history cases and seven funding cases now pass. Both upgrade entrypoints preserve the proxy, staking balances, roles, authorizations, metadata, and stored skipList values; deleting skipList still fails storage validation before a transaction. Relative, absolute, default, and environment funding modes still select the intended provider, including with shadow files on PATH.
  • Integration: the complete Hardhat suite passed: 424 tests with local gas reporting disabled. This runs npm run prepack, creates an actual npm tarball, verifies the shipped manifest, and exercises external-artifact consumers. The operator subset passed all 26 tests. All upgrade transactions were confined to in-process Hardhat networks.

The repository-wide npm run format still reports 58 pre-existing style errors in the unchanged create-operator-keystore.js, create-spv-maintainer-keystore.js, and get-operator-key.js scripts; each is byte-identical to the prior PR head. The OpenZeppelin manifest retains its generated JSON formatting, as configured by the repository's ignore rules.

Compatibility: OpenZeppelin 1.14 has one admin per manifest. The active entry now matches the currently recorded Sepolia proxy; older proxies with the historical admin require their matching historical checkout/manifest. External consumers retain their own manifest and must incorporate the recovered history as documented. No validation bypass or automatic manifest overwrite was added.

GitHub CI passed for head 3620f4dab75fe7ce94a7db2ad51e954abc2e8b71: build/tests, deployment dry run, and Slither, format workflow, and documentation preview. The CI test log confirms 424 passing; Slither reports 0 results. Public deployment and release-only jobs were skipped as expected. No public-network deployment or transfer was performed.

@mswilkison

mswilkison commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Addressed both operator-tooling findings in 594bf49.

  • setup-multiple-operators.sh now anchors relative OPERATORS_CONFIG paths before checking and sourcing them, and uses explicit filesystem paths for shared .env and generated .env.operator-* files. run-new-operator-setup.sh does the same for both of its configuration loads. Each command retains its original working-directory behavior. Usage examples in these scripts and fund-new-operator.sh also use explicit paths.
  • Both ETH transfers now send the quoted _eth_per_operator_wei_dec value used for preflight. Unitless 0.05 sends 50000000000000000 wei to each wallet; unitless 1 sends 1000000000000000000 wei. The existing amount parser and balance requirements are preserved.

Added 19 regression cases in test/scripts/OperatorConfiguration.test.js, using complete script execution with local transaction/generation stubs and dummy credentials. Before the patch, the harness reproduced loading shadow files from PATH and forwarding raw funding values instead of the checked wei amount. Those triggers now pass. Controls cover default, absolute, relative, parent-relative, space-containing and leading-dash paths; automatic existing mode; generated-file absence; invocation-directory and environment-only setup; default/suffixed and unitless funding amounts; hex conversion output; multiple operators; and stopping before generation or transfers for invalid amounts or insufficient ETH.

Verification, in order:

  • bash -n on each of the three changed shell scripts, ESLint for the new test file, Prettier for all four changed files, and git diff --check: passed.
  • node node_modules/mocha/bin/_mocha test/scripts/OperatorConfiguration.test.js test/scripts/OperatorSetup.test.js: 45 passing (19 new, 26 existing).
  • Full Hardhat suite through hre.run("test", { noCompile: true }) with local gas reporting disabled: 443 passing, including the actual package/prepack and external deployment tests.
  • Independent candidate review: no surviving source-backed issue found. The reviewer also passed the 45 operator checks, Bash 3.2 checks for unusual filenames and symlinks, and offline Cast amount conversions.

The previously reported repository-wide formatting errors in unchanged keystore scripts are outside this patch; changed-file checks pass. No public-chain deployment or transfer was performed.

GitHub CI passed for head 594bf497b40a8ad34ac061399e7997c5a120cdcf: Solidity checks, format workflow, and documentation preview. The CI test log confirms 443 passing; Slither reports 0 results. The first change-detector attempt encountered GitHub's temporary “diff unavailable due to heavy server load” error; retrying only that job passed without a code change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants