An upgrade dry run is the process of simulating a smart contract upgrade in a forked or test environment that mirrors the state of the mainnet. This is a critical security practice for protocols using upgradeable contracts, as it allows developers to verify that the new logic deploys correctly, initializes properly, and interacts with existing storage and dependencies without unintended side effects. Unlike standard unit tests, a dry run validates the upgrade path itself, catching issues like storage layout incompatibilities, initialization function failures, or broken integrations with other on-chain components before any real user funds are at risk.
How to Run Upgrade Dry Runs
Introduction to Upgrade Dry Runs
A guide to simulating and validating smart contract upgrades before mainnet deployment to prevent critical failures.
To execute a dry run, you typically use a tool like Hardhat or Foundry to fork the target blockchain at a specific block. This creates a local sandbox with an exact copy of the live contract state. You then deploy the new implementation contract and simulate the upgrade via the proxy's admin functions. Key checks during this process include verifying that all state variables are preserved, all external and internal function calls succeed, and that access control permissions (like the onlyOwner modifier) are correctly enforced in the new version. This step is essential for catching storage collisions, where new variables inadvertently overwrite existing data slots.
For developers using common upgrade patterns like the Transparent Proxy or UUPS, specific considerations apply. With UUPS, you must ensure the upgrade function (upgradeTo) is present and correctly implemented in the new logic contract. For both patterns, you must verify that the proxy's storage layout remains consistent; adding new variables must always be appended to the end of the existing layout. A failed dry run might reveal that a migrate function reverts due to a gas limit or that a newly introduced library address is incorrectly set. Tools like OpenZeppelin Upgrades Plugins can automate many of these checks.
Beyond basic functionality, a comprehensive dry run should include integration testing with the broader protocol ecosystem. This means simulating user interactions—such as deposits, swaps, or governance votes—both before and after the upgrade simulation to ensure no core user flows are broken. It's also advisable to run the upgrade on a public testnet after the local dry run succeeds, as this exposes the new contracts to real gas costs and potential front-running vectors. The final output of a successful dry run is a high-confidence deployment script and a verified set of post-upgrade contract addresses for the mainnet proposal.
Prerequisites for Running a Dry Run
Before you can safely test a smart contract upgrade, you must configure your environment with the correct tools and access.
A dry run simulates a smart contract upgrade on a forked version of the target network without broadcasting any transactions. This requires three core components: a local development environment (like Foundry or Hardhat), access to a forking node provider (such as Alchemy or Infura), and the proposed upgrade payload. You will also need the private keys for the relevant administrative accounts to sign the simulation, though no real funds are spent.
First, ensure your development framework is configured for mainnet forking. For example, in a Foundry project, your foundry.toml file must specify an RPC URL for the chain you're targeting (e.g., Ethereum Mainnet) and enable forking. You'll need an API key from a node service provider. The command anvil --fork-url $RPC_URL creates a local Anvil instance that mirrors the live state, which is the sandbox for your dry run.
Next, you must have the exact upgrade payload ready. This is the encoded transaction data that would be sent to the proxy admin or governance contract to execute the upgrade. It typically includes the address of the new implementation contract and any initialization call data. This payload is usually generated by tools like OpenZeppelin Upgrades Plugins or your project's deployment scripts.
Finally, access to the private keys of the upgrade authority is essential for signing the simulated transaction. While the dry run occurs off-chain, the simulation must validate signatures and permissions as the real transaction would. Use environment variables (e.g., PRIVATE_KEY) to load these keys securely into your script. Never hardcode private keys.
With these prerequisites met—a forked network, the upgrade payload, and authorized signer keys—you can execute a dry run script. This script will deploy the new implementation to the fork, call the upgrade function, and trace the execution to identify any reverts, gas cost issues, or state changes, all without any on-chain risk.
Key Concepts for Upgrade Testing
Learn the essential steps and tools for safely testing smart contract upgrades before deploying to mainnet.
Dry Run Methods by Blockchain
Hardhat Forking
Ethereum developers primarily use state forking to simulate upgrades on a local copy of mainnet. The most common tool is Hardhat's hardhat_reset RPC method.
Key Steps:
- Fork mainnet at a specific block:
npx hardhat node --fork https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY - Deploy your upgrade contract to the forked network.
- Use
hardhat_resetto revert the fork to its original state for repeated tests.
Example Hardhat Configuration:
javascriptmodule.exports = { networks: { hardhat: { forking: { url: process.env.MAINNET_RPC_URL, blockNumber: 19238210 } } } };
This method allows you to test interactions with live protocols like Uniswap or Aave before deploying.
Step-by-Step: Ethereum Upgrade Dry Run
A practical guide for developers and node operators to safely test and validate Ethereum client upgrades before mainnet deployment.
An Ethereum upgrade dry run is the process of simulating a network hard fork or client update in a controlled, isolated environment. This is a critical step for node operators, client teams, and application developers to verify that new software versions function correctly, are compatible with existing infrastructure, and do not introduce consensus failures. Running a dry run on a dedicated testnet or a local devnet allows you to identify and resolve issues without risking real funds or disrupting the live Ethereum network. It is a mandatory best practice for any significant protocol change, such as the transition from Proof-of-Work to Proof-of-Stake or the implementation of new EIPs.
To begin a dry run, you must first select the appropriate testing environment. For testing client upgrades, the Sepolia or Holesky testnets are ideal as they mimic mainnet conditions. For testing entirely new fork logic or custom parameters, you can spin up a local development network using tools like geth --dev or a multi-client devnet using the Ethereum Foundation's Launchpad. Ensure all participants in the test—other nodes, validators, and tooling—are running the candidate upgrade version. The goal is to create a replica of the intended mainnet upgrade scenario.
The core of the dry run involves monitoring the network through the upgrade's activation epoch or block height. Use your client's logs and metrics (e.g., geth's console, teku's log output, Prometheus metrics) to watch for consensus failures, block production halts, or syncing issues. Key checks include verifying that the chain continues to finalize, that validators assigned to the new duties perform them correctly, and that the state transition logic for the new fork rules is applied. Any divergence in chain head between clients running the same version indicates a critical bug that must be addressed before mainnet deployment.
Beyond consensus, you must test the integration with your operational stack. This includes staking infrastructure (validator clients, key management), monitoring and alerting systems, RPC endpoints for dApps, and block explorers. Ensure your upgrade and rollback procedures—such as database migrations or reverting to a previous client version—are documented and tested. A successful dry run is not just about the chain progressing; it's about your entire node setup remaining stable and functional through the transition. This step often uncovers operational issues not present in unit tests.
Finally, participate in or monitor coordinated shadow forks. These are long-running testnets that replay mainnet state and traffic, providing the most realistic pre-production environment. Client teams like Nethermind and Besu frequently run these. After completing your dry run, compile a report detailing the client version, test environment, any issues encountered, and their resolutions. Sharing findings with client teams and the community on forums like Ethereum R&D Discord or GitHub issues contributes to the overall security and smooth execution of the Ethereum upgrade.
Step-by-Step: Solana Upgrade Dry Run
A practical guide to executing a local test of a Solana network upgrade before applying it to your mainnet validator. This process is critical for identifying potential issues and ensuring a smooth transition.
A Solana upgrade dry run is the process of simulating a network upgrade on a local test environment that mirrors the mainnet state. This allows validator operators to verify that their node configuration, custom programs, and tooling will function correctly with the new runtime version. The primary tool for this is the solana-test-validator, which can be configured to load a snapshot of the mainnet ledger and apply a specific target software version. Running a dry run is considered a best practice for any major or minor upgrade, especially those involving changes to the Solana runtime, transaction processing, or system programs.
To begin, you need to install the target version of the Solana CLI tools you intend to test. Use solana-install init to switch versions. For example, to test v1.18.0, you would run solana-install init 1.18.0. Next, you must obtain a recent snapshot of the mainnet ledger. The Solana Foundation provides public snapshot archives. You can download one using a command like wget -r -l1 -A "snapshot-*.tar.zst" -nH --cut-dirs=2 https://snapshots.mainnet-beta.solana.com/. Ensure you have sufficient disk space, as a full snapshot can be over 500GB.
Launch the test validator with the snapshot and the desired runtime version. The key command includes the --limit-ledger-size flag to manage disk usage and the --clone parameter to replicate specific accounts (like your validator's vote account). An example command is:
bashsolana-test-validator \ --ledger ./test-ledger \ --reset \ --limit-ledger-size 500000 \ --snapshot /path/to/snapshot.tar.zst \ --clone <YOUR_VOTE_ACCOUNT_PUBKEY> \ --bpf-program <PROGRAM_PUBKEY> <PATH_TO_SO_FILE> \ --rpc-port 8899
This starts a local RPC endpoint where you can interact with the cloned state.
Once the validator is running, you should perform a series of functional tests. This includes: sending transactions, staking SOL, voting, interacting with your deployed smart contracts, and using any monitoring or automation scripts. Monitor the validator logs for errors, warnings, or panics. Pay special attention to the execution of your custom programs loaded via --bpf-program. The goal is to ensure all critical operations succeed under the new software version. This phase may reveal incompatibilities that require program redeployment or configuration changes.
After successful testing, you must cleanly stop the validator and analyze the results. A critical final step is to verify the upgrade mechanism itself by simulating the on-chain process. You can use the solana-validator tool with the --require-tower and --hard-fork flags on your local network to test the consensus transition. Document any issues encountered and their resolutions. This dry run process significantly de-risks the mainnet upgrade, protecting your stake and contributing to network stability. Always consult the official Solana Upgrade Documentation for version-specific instructions.
Upgrade Simulation Tool Comparison
A feature and performance comparison of tools for simulating smart contract upgrades on EVM chains.
| Feature / Metric | Chainscore Upgrade Simulator | Tenderly Fork | Hardhat Fork |
|---|---|---|---|
Simulation Environment | Deterministic sandbox | Forked mainnet node | Local Hardhat Network |
Execution Speed | < 2 seconds | 30-60 seconds | 5-10 seconds |
State Persistence Between Runs | |||
Gas Usage Estimation | |||
Storage Layout Diff Analysis | |||
Automated Upgrade Safety Checks | |||
Cost per Simulation | Free | $0.10-0.50 (est.) | Free |
Requires RPC Endpoint / API Key | |||
Supports Custom Solidity Versions | 0.4.11 - 0.8.x | Node-dependent | Configurable |
Common Dry Run Mistakes to Avoid
Dry running a smart contract upgrade is a critical safety step, but common pitfalls can lead to false confidence. This guide covers frequent errors and how to fix them.
A passing dry run that fails on mainnet often indicates a state dependency or environmental difference. Dry runs typically execute against a simulated or forked state, which may not match the live contract's exact conditions.
Key differences to check:
- Storage layout mismatches: The new contract's storage variables must be compatible. Use
slither-check-upgradeabilityorhardhat-upgradesvalidation. - Missing constructor logic: Proxy constructors are not called on upgrade. Initialization must be in an
initializefunction. - Gas limit differences: The forked testnet may have a higher gas limit than the target block's limit. Always check
gasUsedin the dry run receipt. - Chain-specific addresses: Hardcoded oracle or manager addresses (e.g.,
0x...) may differ between networks.
Essential Resources and Documentation
These resources explain how to run upgrade dry runs across common blockchain stacks. Each card focuses on concrete tooling and documented workflows used by protocol teams to validate upgrades before production execution.
Frequently Asked Questions
Common questions and troubleshooting steps for developers preparing and executing upgrade simulations on Ethereum smart contracts.
An upgrade dry run is a simulation of a smart contract upgrade process on a forked version of the Ethereum mainnet or testnet. It is a critical security practice that allows developers to test the deployment and initialization logic of a new implementation contract without broadcasting any real transactions to the live network.
This process is necessary to:
- Verify correctness: Ensure the new contract bytecode deploys correctly and the proxy points to it.
- Test initialization: Confirm that the
initializeor migration function executes without errors and sets the correct initial state. - Estimate gas costs: Get accurate gas estimates for the actual upgrade transaction.
- Catch integration failures: Identify issues with storage layout compatibility or function selectors before risking mainnet funds.
Tools like Hardhat, Foundry, and Tenderly are commonly used to create a local fork and execute the dry run.
Conclusion and Next Steps
You have successfully learned how to execute a controlled upgrade dry run using Chainscore's Forge. This final section summarizes key takeaways and outlines the next steps for integrating this process into your development workflow.
Running a dry run is a non-negotiable step for secure smart contract upgrades. It allows you to validate the upgrade path in a production-like environment without risking live assets or disrupting service. The primary goal is to catch potential issues—such as storage layout conflicts, initialization errors, or unexpected side effects—before they reach mainnet. By using a dedicated testnet or a forked mainnet state, you simulate the exact conditions your users will experience.
To operationalize this practice, integrate upgrade dry runs into your CI/CD pipeline. Tools like Foundry's forge script or Hardhat tasks can automate the deployment and verification steps you performed manually. Consider setting up a staging environment that mirrors your mainnet configuration, including dependencies on external protocols like Chainlink oracles or other DeFi contracts. Automating these checks ensures every proposed upgrade is rigorously tested, reducing human error and increasing deployment confidence.
Your next steps should involve exploring more advanced validation techniques. Investigate tools like Slither or MythX for static analysis of your upgrade diff to detect security vulnerabilities. For complex upgrades, consider formal verification using the Certora Prover to mathematically prove critical properties are preserved. Finally, document your upgrade procedure and establish a clear rollback plan. A well-documented process, combined with the empirical confidence gained from dry runs, forms the foundation of a robust and secure upgrade lifecycle for any protocol.