An upgradable smart contract is one whose logic can be replaced after deployment, even though the code already written to the blockchain can never be edited in place. Developers work around that limit by splitting an application in two: a permanent address that users and other contracts talk to, and a separate “logic” contract behind it that can be swapped. Change the logic, and the behavior changes while the address and all stored data stay exactly where they are.
That design answers a genuine problem. A normal contract is immutable by default, so one overlooked bug can freeze funds with no way to patch it. It also creates a new problem: whoever can swap the logic can change the rules. Most of this guide is about that tension — how the swap works mechanically, which patterns exist, who actually uses them, and when the added risk is worth taking.
How Can an Immutable Contract Be Changed?
Strictly speaking, it can’t. The deployed bytecode is fixed. What changes is which contract the fixed entry point actually runs.
Upgradable smart contracts rely on a split between a proxy and an implementation. The proxy is the contract users interact with. It holds the application’s data and keeps the same address forever. It contains almost no business logic of its own. The implementation (also called the logic contract) holds the actual code, and it can be redeployed and pointed to again whenever the team ships a new version.
The proxy and the implementation

The proxy stores one critical piece of information: the address of the current implementation. When a call comes in that the proxy doesn’t recognize, its fallback function forwards that call to the implementation. To upgrade, you deploy a new implementation and update the stored address. Users keep calling the same proxy, and their balances and settings — which live in the proxy’s own storage — are untouched.
To keep that implementation address from clashing with the application’s own variables, OpenZeppelin and most of the ecosystem store it in a fixed, pseudo-random slot defined by EIP-1967. That standard is also why block explorers can detect a proxy and offer a “Read as Proxy” view.
What does delegatecall actually do?
delegatecall is the opcode that makes the whole pattern possible. It runs the implementation’s code inside the proxy’s storage context, so any state the logic reads or writes is the proxy’s state, not the implementation’s. msg.sender and msg.value are preserved as well, so the logic behaves as if the user called it directly.
This has one awkward consequence. A constructor runs only when a contract is deployed, in that contract’s own context — which means it never runs for the proxy. Upgradable contracts replace the constructor with an initialize() function that the proxy calls once, guarded so it can’t be run twice. Getting this wrong is a common source of trouble, which is why the deeper patterns below exist.
Why Would You Make a Contract Changeable?
The short answer: to fix mistakes and ship improvements without abandoning the contract users already trust.
Without upgradability, the only way to change a live protocol is to deploy a brand-new contract and migrate every user, balance, and integration to the new address. That is slow, expensive in gas, and risky — anyone who misses the migration can be left interacting with a dead contract. Keeping the same proxy address sidesteps all of that. Wallets, aggregators, and other protocols that hard-coded the address keep working after an upgrade.
There are three recurring reasons teams reach for it:
- Bug and security fixes. A patch can be deployed in one transaction instead of a full migration.
- New features. Protocols add functions over time without forcing users to move funds.
- Data continuity. Balances and configuration stay in the proxy, so there is no state to copy.
Who Relies on Changeable Contracts?
Mostly teams running protocols that hold value and expect to evolve. DeFi lending markets, decentralized exchanges, and many stablecoins ship as upgradable contracts because a frozen bug in a money protocol is far more damaging than the governance overhead of being able to patch it.
DAOs use upgradability so that token-holder votes can authorize changes to the protocol they govern. NFT projects and on-chain games use it to add mechanics after launch. Cross-chain bridges, which have been frequent hacking targets, often keep an upgrade path open so a discovered flaw can be closed quickly.
The common thread is a contract that will live for years and manage assets. A short-lived contract, or one deliberately designed to be trustless and final, is usually better off immutable — more on that trade-off later.
Proxy Patterns Compared: Transparent, UUPS, Beacon, and Diamond
Four patterns dominate in 2026. They all use a proxy and delegatecall; they differ in where the upgrade logic lives and how many contracts one upgrade touches.
| Pattern | How the upgrade happens | Fits best when | Main trade-off |
|---|---|---|---|
| Transparent | An admin calls an upgrade function held in the proxy; OpenZeppelin v5 deploys a dedicated admin contract automatically | You want a well-worn, simple setup | Higher deployment cost; upgrade logic sits in every proxy |
| UUPS | The upgrade function lives in the logic contract and is triggered through it | You deploy many proxies and want each one cheap | Shipping logic without the upgrade code can lock upgrades forever |
| Beacon | Many proxies read one shared beacon that stores the logic address; you update the beacon once | You run many identical instances (per-user vaults, repeated deployments) | The beacon is a single point of control and failure |
| Diamond (EIP-2535) | A router maps individual function calls to multiple “facet” contracts, edited with diamondCut | A contract outgrows the 24 KB size limit or needs modular upgrades | More moving parts and heavier tooling |
Transparent proxy
The transparent pattern was the original mainstream approach. It separates admin calls from user calls inside the proxy: if the admin calls it, the proxy handles upgrade logic; if anyone else calls it, the call passes through to the implementation. That split exists to prevent function selector clashes, where a function on the proxy and a function on the implementation accidentally share a signature.
In OpenZeppelin Contracts v5, the TransparentUpgradeableProxy deploys its own ProxyAdmin contract at construction, and that admin is fixed for the life of the proxy. The older upgradeTo call was also dropped in favor of a single upgradeToAndCall. The pattern is simple to reason about, but it costs more gas to deploy than the alternatives.
UUPS
UUPS (Universal Upgradeable Proxy Standard, from EIP-1822) moves the upgrade logic out of the proxy and into the implementation. The proxy becomes a thin, cheap contract that only delegates. OpenZeppelin now recommends UUPS for most new projects because it is more gas-efficient to deploy.
There is a catch worth stating plainly. Because the upgrade function lives in the logic, every new implementation has to keep that function — ship one that forgets it and the contract can no longer be upgraded. OpenZeppelin builds in a safety check to block that mistake. It also means upgradability is optional over time: as OpenZeppelin’s documentation puts it, in UUPS proxies “the upgrade is handled by the implementation, and can eventually be removed.” A team can deliberately deploy a final version with no upgrade path and make the contract permanently immutable.
Beacon proxy
A beacon proxy adds one more layer. Instead of each proxy storing its own implementation address, many proxies point to a single beacon contract that stores it. Upgrade the beacon once, and every proxy reading from it switches to the new logic in the same moment.
This is efficient when you deploy the same contract many times — one vault per user, for example. The flip side is concentration: a single beacon controls a whole fleet, so a mistake or a compromised key affects all of them at once.
Diamond (EIP-2535)

The Diamond standard is a finalized EIP for contracts that have grown too large or too modular for a single implementation. A “diamond” is a proxy that routes each function call to one of several facets — separate logic contracts — using a mapping edited through a diamondCut function. There is no practical limit on how many facets a diamond can hold, which helps contracts bump up against Ethereum’s 24 KB code-size ceiling.
The payoff is granular upgrades: you can replace one facet without touching the rest. The cost is complexity. Diamonds introduce their own storage conventions and tooling, and they are harder to audit than a plain proxy.
How Do You Build One Without Breaking It?
Most teams don’t hand-write proxies. They use the OpenZeppelin Upgrades Plugins for Hardhat or Foundry, which deploy the proxy and implementation together and — importantly — check each new version’s storage layout against the old one before allowing the upgrade.
A typical flow looks like this:
- Write the logic contract with an
initialize()function instead of a constructor, and disable the implementation’s own initializers so it can’t be hijacked. - Deploy it behind a proxy using the plugin, which wires up the proxy, implementation, and admin for you.
- When you need changes, write a new version that only adds storage variables, never reorders or removes existing ones.
- Run the upgrade through the plugin, which validates the storage layout and swaps the implementation.
Current tooling targets the Solidity 0.8.x line (0.8.37 is the latest release as of September 2026) and OpenZeppelin Contracts v5, which require Solidity ^0.8.20.
Deploying and upgrading also means reaching the network from your scripts and monitoring. Sending the upgrade transaction, reading a live proxy’s current implementation address, or watching for an unexpected change all require API access to the chain. A provider such as NOWNodes supplies that access to Ethereum and other networks, so your deployment pipeline and alerts can query a contract and broadcast transactions without your team maintaining its own infrastructure.
What Are the Risks and Trade-offs?
Upgradability is power, and power is the risk. If a key can change the logic, that key can also drain or brick the protocol. The question for any upgradable contract is not just “can it be upgraded?” but “who can upgrade it, and under what checks?”
The main failure modes:
- Centralized control. A single externally owned account holding the upgrade key is a rug-pull waiting to happen. Serious projects put the upgrade behind a multisig, a timelock that delays changes so users can react, or on-chain governance — and some eventually renounce upgradability entirely.
- Storage collisions. If a new implementation reorders or removes variables, it reads and writes the wrong storage slots and corrupts data. This is exactly what the OpenZeppelin plugin’s layout check guards against.
- Uninitialized implementations. An implementation left uninitialized can sometimes be seized by an attacker. The most famous example is the 2017 Parity multisig freeze, where a user triggered a shared library contract’s self-destruct and permanently locked roughly 513,000 ETH — worth about $150 million at the time — across every wallet that depended on it. Modern practice disables an implementation’s initializers at deployment specifically to prevent that class of takeover.
- Audit scope. Upgradability adds admin logic, proxies, and initializers that a review has to cover. A thorough audit before launch is standard for any contract holding meaningful value.
None of these are reasons to avoid upgradable smart contracts outright. They are reasons to design the upgrade process as carefully as the application itself.
Conclusion
Upgradable smart contracts are a deliberate compromise. You give up the blockchain’s default immutability in exchange for the ability to patch bugs and add features without migrating users — and in return you take on the responsibility of controlling who can make those changes.
For a protocol that holds funds and expects to evolve, that trade usually makes sense, provided the upgrade key sits behind a timelock or multisig rather than one person’s wallet. For a contract meant to be final and trustless, immutability is the stronger guarantee. A reasonable default is the least power necessary: start upgradable if you need to, protect the upgrade path, and remove it once the design has settled. Choosing between the transparent, UUPS, beacon, and diamond patterns then comes down to how many contracts you run and how modular you need them to be.
FAQ
Does upgrading a smart contract change its address?
No — keeping the same address is the entire point. Users and integrations interact with the proxy, which never moves. Only the implementation address stored inside the proxy changes, and that happens behind the scenes.
How can you tell if a deployed contract is upgradeable?
Check whether it’s a proxy. Block explorers read the EIP-1967 storage slot and flag proxy contracts, usually offering a “Read as Proxy” or “Write as Proxy” option and a link to the current implementation. If those appear, the contract is almost certainly upgradeable.
Can a contract be made upgradeable after it is already live?
Not in place. Upgradability has to be designed in from the start by deploying behind a proxy. An existing immutable contract can’t grow a proxy later; the only path is to deploy a new upgradable version and migrate state and users to it.
How is an upgrade different from a contract migration?
An upgrade swaps the logic while the address and stored data stay put, so nothing has to move. A migration deploys an entirely new contract at a new address and copies balances and settings over, which is slower, costs more gas, and risks leaving users on the old address. Upgradability exists largely to avoid that process.



