If the proxy pattern carries so much risk, why doesn't the industry just abandon upgradeable design entirely and switch to immutable contracts?
Because immutable contracts carry their own cost, just in a different form. If a protocol manages hundreds of millions of dollars in assets but its code can never be modified, then the moment any logic flaw is discovered later (even a small one), the only remedy is deploying an entirely new contract and asking every user to manually migrate their assets — a process that's inherently slow and full of friction, and users may end up stranded in the flawed old version out of neglect or distrust of the new contract. The actual risk carried isn't necessarily lower than what a proxy pattern would have exposed.
That's also why the industry hasn't settled on a consensus of "upgradeable vs. immutable, which is unconditionally better" — the choice tends to be framed instead as a tradeoff: how quickly does this protocol need to be able to react and fix a bug, versus how much decentralization is it willing to sacrifice for that upgrade flexibility. Core contracts like Uniswap's choose immutability because the protocol's logic is relatively stable and the community places heavy weight on the promise that "once deployed, the contract's rules can't be unilaterally changed." Meanwhile, many newer protocols still iterating rapidly tend to choose the proxy pattern, trading centralized upgrade capability for the ability to respond quickly to an emerging vulnerability.
What does "the library contract wasn't initialized" in the Parity incident actually mean? Why did the lack of initialization cause such a severe outcome?
In an upgradeable contract architecture, the implementation contract's (or library contract's) own constructor typically isn't used directly, because the proxy contract borrows the implementation's code via DELEGATECALL while using its own storage — meaning the implementation contract's own storage state should, in theory, stay blank, and the actual initialization needs to happen through a separately called function, usually named initialize(), invoked manually after deployment. That's exactly where the problem lies: if a developer forgets to call this initialization function, or if the function isn't properly guarded to only be callable once, anyone can potentially call it first and set themselves as the contract's owner.
In the Parity incident, it was precisely because this library contract's initialization function had never been properly locked down that a user (most likely out of curiosity or by accident) called the initialization function, set themselves as the owner, and then triggered the contract's built-in selfdestruct function, wiping the entire library's code off the chain. Because every proxy wallet depending on this library was essentially just forwarding calls and storing no logic of its own, once the library vanished, those wallets became empty shells pointing at nothing — with no remaining code anywhere able to read or move the assets inside them.
Beyond checking for "Read as Proxy" on Etherscan, are there other ways for an ordinary user to judge whether a proxy contract's upgrade authority is being properly managed?
Beyond confirming whether the upgrade-controlling address is a multisig wallet, you can dig further into that multisig's signing threshold — for example, "3 of 5 signers required" versus "1 of 2 signers required" represents very different security levels; if the threshold is set too low, a multisig provides barely more protection than a single Private Key. This information is usually visible in the official interface of the multisig tool being used (Gnosis Safe, for example), which lists the current signer set and the required threshold.
Another worthwhile thing to check is whether there's a timelock mechanism — a timelock means that even after an upgrade instruction has been submitted, it must wait through a publicly visible delay period (say, 48 hours) before actually taking effect. During that window, the community or users can, in theory, observe what the upcoming upgrade actually contains, and if something looks off, still have a chance to withdraw assets in time. If a protocol's upgrades take effect the instant a transaction is submitted, users have zero reaction time — a fundamentally different risk tier from a protocol with timelock protection. This information typically requires cross-referencing contract code, official documentation, and governance forums — no single page summarizes it directly — but it's worth taking the time to confirm before depositing a significant amount of assets.
If I discover a protocol holding my assets uses an upgradeable contract with upgrade authority that isn't a multisig, should I withdraw immediately?
Not necessarily a panic-withdraw situation, but it genuinely is a risk signal worth taking seriously — not a technical detail you can ignore. An upgradeable contract by itself doesn't equal dangerous — many reputable, long-running protocols also use the proxy pattern. The key distinction is the quality of governance over the upgrade authority, not the binary question of whether upgradeable design was used at all. If the upgrade authority, while not a multisig, is still controlled by a well-known institution's wallet with a publicly transparent governance track record, the risk assessment looks very different from a controlling address that's fully anonymous with no verifiable background.
A more practical approach is folding "quality of upgrade-authority governance" into whatever due-diligence checklist you already use — placing it alongside audit reports, TVL size, and team background, rather than treating it as a standalone black-or-white verdict. If, after that assessment, the risk sits beyond what you're comfortable with, gradually reducing your exposure is more practical than liquidating your entire position over a single risk factor. But if you find concentrated upgrade authority stacked together with no timelock and an unverifiable team background, that combination of multiple risk factors genuinely warrants heightened caution and seriously considering reducing your position.
Smart contracts have a fundamental property by design: once deployed, the code can't be changed — this is the foundation of the "immutability" trust guarantee. But real-world protocols need ongoing bug fixes and new features, so the "proxy pattern" became the industry-standard solution — letting a contract's underlying logic stay swappable beneath a surface that appears immutable. This article breaks down how the proxy pattern actually works, why it's simultaneously a source of convenience and risk, and uses three real incidents with real losses to show exactly how this design choice can go wrong.
Every upgradeable contract relies on the same underlying mechanism: the DELEGATECALL opcode. When a user calls a function on a proxy contract, the proxy doesn't execute the logic itself — it forwards the call via DELEGATECALL to a separate "implementation contract," but executes it using the proxy's own storage. This means the implementation contract's code logic can be swapped for a new version at any time, and as long as the new version's logic remains attached to the same proxy address, the user's call entry point and where assets are stored never need to change. That's precisely what makes "upgrading" possible without breaking the invariance of the contract address.
There are currently three main ways the industry implements proxy patterns, each with a different risk profile. UUPS (EIP-1822) puts the upgrade logic inside the implementation contract itself, keeping the proxy minimal — the upside is gas savings, but the cost is that every new implementation version must correctly retain the upgrade function; if a deployment accidentally omits it, the contract gets permanently locked and can never be upgraded again. Transparent Proxy, popularized by OpenZeppelin, separates admin privileges from ordinary user calls to avoid function selector clashes, but the upgrade authority concentrates in a single admin address — once that address is compromised, an attacker can directly swap out the logic contract. Beacon Proxy introduces a third component — a beacon contract — where multiple proxy instances share the same beacon; upgrading the beacon once synchronizes the update across every dependent proxy, well-suited for deploying many instances of identical logic at scale, but the cost is that "control over the beacon" effectively becomes root-level privilege over the entire system — if the beacon gets upgraded to a malicious version, every proxy that depends on it gets hit simultaneously.
The 2017 Parity multisig wallet incident was one of the earliest, and most painful, lessons in this design pattern: a library contract shared across multiple wallets, because it had never been properly initialized, was accidentally triggered into a selfdestruct by a user — instantly erasing the library, and because every proxy wallet depending on it now pointed to a logic contract that no longer existed, they permanently lost any executable code. Roughly $300 million in assets was locked forever, unable to be moved. The 2024 Radiant Capital incident demonstrated the direct consequence of compromised proxy authority: attackers compromised multisig signers, gaining the ability to upgrade the lending pool contract, executed transferOwnership(), and swapped in a malicious implementation, causing about $50 million in losses. The IoTeX Cross-Chain Bridge incident that same year was even more direct — attackers simply called the upgrade() function and replaced the implementation contract outright with a malicious version stripped of all security checks, causing about $4.4 million in losses. The three incidents' methods differed, but they share a common thread: the proxy pattern reduces the question of "who can change a contract's logic" down to "who controls that one upgrade key."
Next time you're about to deposit assets into a DeFi protocol, beyond checking whether it's been live for a while and been audited, it's worth doing one extra step: look up the contract address on Etherscan and check whether the "Contract" tab shows "Read as Proxy." If it does, this is an upgradeable contract — trace the implementation address it points to, and check which address currently holds the upgrade authority. If the controlling address is a regular externally-owned account (EOA) rather than a multisig wallet, or if there's no visible timelock mechanism preventing an upgrade from taking effect instantly, that means this protocol's core logic could, in theory, be entirely rewritten by a single Private Key with zero warning to you — an entirely different risk dimension from whether a contract has been audited. An audit checks whether the current version of the code is written correctly; it has no bearing on whether whatever code gets swapped in later was written by someone trustworthy.