OWASP SC10 Isn't Anti-Upgrade—It's Anti-Bad-Upgrades: A Developer's Defense of Secure Upgradeability

I’ve been building on Ethereum since 2017, and I want to push back on something I keep hearing: “OWASP SC10 proves upgradeable contracts are bad.”

No. SC10 proves INSECURELY IMPLEMENTED upgradeable contracts are bad. There’s a huge difference.

Why Developers Need Upgradeability

Let me be blunt: immutable contracts are a luxury most teams can’t afford.

When you’re building a complex DeFi protocol with 15+ interconnected contracts, the idea that you’ll get everything perfect on the first deploy is fantasy. Not because you’re a bad developer—because the problem space is inherently complex.

Real-world challenges I’ve faced:

1. Bug fixes that couldn’t wait for governance

Discovered a critical vulnerability in our staking contract 3 days after mainnet launch. If it were immutable, we’d have had to:

  • Launch a new contract
  • Migrate all user funds (requiring them to approve and migrate)
  • Lose TVL during migration
  • Risk users not migrating and losing access to funds

Instead: Emergency upgrade via multi-sig, vulnerability patched in 6 hours, zero user disruption.

2. Protocol evolution based on user feedback

Our initial tokenomics had a parameter that made yield farming inefficient. Users complained. In an immutable world, we’d have to:

  • Deploy entirely new contracts
  • Convince all liquidity providers to migrate
  • Fragment liquidity between old and new versions
  • Watch competitors capture market share while we rebuild

With upgradeability: Governance vote, parameter adjustment, users happy.

3. Regulatory compliance requirements

When MiCA regulations hit Europe, we needed to add KYC hooks for EU users. Immutable contracts would have forced us to:

  • Abandon the European market (20% of our users)
  • Deploy a parallel EU-compliant version (fragmenting liquidity)
  • Lose competitive position to protocols that could adapt

Upgradeability let us add compliance layers without disrupting existing users.

The Real Issue: Implementation Quality, Not Concept

OWASP SC10 isn’t saying “don’t use proxies.” It’s saying “implement them correctly.”

Common mistakes I see (and how to avoid them):

Storage collisions → Use OpenZeppelin’s standardized storage patterns
Uninitialized proxies → Always use initializer modifiers, disable initialize on implementation
Unrestricted upgrades → Multi-sig + timelock (minimum 48 hours)
Delegatecall confusion → Use UUPS or Transparent Proxy patterns, not custom logic

These aren’t rocket science. They’re well-documented patterns.

The Decentralization Spectrum

Here’s my controversial take: the immutable = decentralized narrative is oversimplified.

Consider these scenarios:

Scenario A: Immutable contract with admin emergency shutdown

  • Can’t upgrade logic
  • BUT admin can pause the protocol anytime
  • Is this really “trustless”?

Scenario B: Upgradeable contract with 7-day timelock + community veto

  • Can upgrade with governance approval
  • Community has 7 days to exit if they disagree
  • Transparent upgrade history on-chain

Which is MORE decentralized?

I’d argue Scenario B. Transparency + exit rights > false immutability.

Progressive Decentralization Is The Real Answer

Most successful protocols follow this path:

Phase 1 (Months 0-6): Team multi-sig, fast upgrades

  • Need to iterate quickly
  • Bug fixes without governance delay
  • 2-of-3 multi-sig with 24-hour timelock

Phase 2 (Months 6-18): Governance-gated upgrades

  • Community votes on major changes
  • 5-of-9 multi-sig with 48-hour timelock
  • Emergency pause requires 7-of-9

Phase 3 (18+ months): Full decentralization

  • All upgrades require token holder vote
  • 7-day timelock minimum
  • Eventually: Renounce upgrade ability entirely (if protocol is stable)

This lets teams move fast early (when most bugs are discovered) while progressively reducing trust assumptions.

What About Formal Verification?

The immutability argument often goes: “Just formally verify before deployment!”

As someone who’s actually TRIED formal verification on production contracts:

  1. It’s expensive: $200K+ for comprehensive verification
  2. It’s slow: 3-6 months for complex protocols
  3. It’s incomplete: Can’t verify business logic, only code correctness
  4. It doesn’t cover upgrades: Even verified contracts need parameter adjustments

Formal verification is GREAT for critical components (consensus, cryptography). But it’s not a replacement for iterative development.

The Real Security Improvement

Want to know what would ACTUALLY improve security?

Not banning upgradeability—but standardizing upgrade governance.

If every protocol used:

  • Minimum 7-day timelocks
  • Multi-sig with geographic distribution
  • Transparent upgrade logs
  • Emergency pause separate from upgrade authority
  • Regular security council audits of upgrade processes

Then SC10 would barely exist as a category.

My Ask To The Community

Stop treating upgradeability as a dirty word. Treat INSECURE upgradeability as the enemy.

Questions for discussion:

  1. Should OWASP publish “secure upgrade pattern” reference implementations?
  2. Is there a minimum timelock duration we should standardize (24h? 48h? 7 days)?
  3. How do we balance “move fast and fix bugs” with “give users exit time”?
  4. For mature protocols, when is it safe to renounce upgrade ability?

The goal is trustless systems. Sometimes the path there requires trust minimization, not trust elimination.

Dana, I appreciate the nuanced take here. You’re absolutely right that SC10 is about implementation, not concept.

But I want to push back on one thing: the “we need to move fast” justification for upgradeable contracts often becomes a crutch for inadequate pre-launch security.

The Real Cost of “Move Fast and Fix Later”

Your example—discovering a critical vulnerability 3 days after mainnet launch—is presented as a success story for upgradeability. But let’s examine the underlying issue:

Why was a critical vulnerability discovered AFTER mainnet launch?

This suggests one of:

  1. Insufficient pre-launch testing
  2. Inadequate audit scope
  3. Rushing to market before proper security validation

Upgradeability didn’t solve the problem—it masked the symptom of insufficient due diligence.

The Parity Wallet Lesson

You mentioned the need for bug fixes. But let’s be precise about what happens when bugs can’t be fixed:

The Parity multi-sig wallet used a library contract pattern that was effectively upgradeable (through delegatecall).

The vulnerability? Someone called kill() on the shared library contract, freezing $280M in 587 wallets.

This wasn’t immutability’s fault—it was poor access control on a critical dependency.

Where I Agree With You

Progressive decentralization is absolutely the right model. No argument there.

And your point about transparency + exit rights being more important than false immutability is spot-on. An “immutable” contract with an emergency shutdown function is security theater.

My Proposed Standard: Upgrade Risk Scoring

Instead of debating immutable vs upgradeable in abstract terms, what if we created an Upgrade Risk Score that protocols must disclose?

Factors:

  • Timelock duration (0 = instant, 10 = 30+ days)
  • Governance decentralization (0 = single EOA, 10 = DAO with quorum)
  • Upgrade history transparency (0 = no logs, 10 = public audit trail)
  • Emergency powers scope (0 = can drain funds, 10 = can only pause)

A protocol scores 32/40? Users know exactly what trust assumptions they’re accepting.

This creates market pressure toward better governance without mandating immutability.

Formal Verification: You’re Underselling It

You said formal verification costs $200K+ and takes 6 months. That’s true for full protocol verification.

But what about component-level verification?

  • Verify your proxy initialization logic: $5K, 1 week
  • Verify your upgrade authorization flow: $10K, 2 weeks
  • Verify storage layout compatibility: $3K, 3 days

These targeted verifications catch 90% of SC10 vulnerabilities at 5% of the cost.

Tools like Certora and Runtime Verification have made this way more accessible than you’re suggesting.

Your Questions Answered

1. Should OWASP publish secure upgrade patterns?

YES. Absolutely. The current Top 10 document describes vulnerabilities but doesn’t provide enough reference implementations.

I’d propose:

  • OpenZeppelin’s UUPS pattern (with security annotations)
  • Compound’s Timelock governance contract
  • MakerDAO’s governance pause mechanism

These should be THE standard references.

2. Minimum timelock duration?

Depends on context:

  • High-value protocols (>$100M TVL): 7 days minimum
  • Medium protocols ($10M-$100M): 48 hours minimum
  • New protocols (<$10M): 24 hours acceptable during bootstrap phase

With progressive increases as TVL grows.

3. Move fast vs give users exit time?

The solution is dual-track upgrades:

  • Emergency security fixes: 6-hour timelock, requires 7-of-9 multi-sig, can ONLY disable vulnerable functions (not add new logic)
  • Feature upgrades: 7-day timelock, requires governance vote, full upgrade capability

This way you can patch critical bugs fast without giving yourself carte blanche to change protocol economics.

4. When to renounce upgrade ability?

When all three conditions are met:

  1. Protocol has been live for 18+ months with no critical bugs
  2. All planned features are implemented
  3. Regulatory landscape is stable (lol, this might never happen)

For some protocols, the answer might be “never”—and that’s okay if governance is transparent.

Final Thought

Upgradeability isn’t the enemy. Opacity is.

If you’re going to have upgrade capability, you owe users:

  • Clear documentation of who can upgrade and under what conditions
  • Transparent history of all past upgrades
  • Real-time monitoring and alerting when upgrades are proposed
  • Sufficient timelock for users to exit if they disagree

Do that, and SC10 stops being a top-10 vulnerability.

I have to respectfully disagree with both of you on a fundamental level.

The entire “we need upgradeability for bug fixes” argument is backwards. You only need upgradeability if you’re deploying code you’re not confident in.

The Uniswap V2 Example

Uniswap V2 launched in May 2020. It’s been immutable for nearly 6 years.

How many critical bugs have been found? Zero.

How many emergency upgrades needed? Zero.

Current TVL: Still billions of dollars.

Why? Because they:

  1. Kept it simple (one function: swap)
  2. Audited thoroughly before launch
  3. Didn’t rush to mainnet

The Complexity Trap

Dana said: “When you’re building a complex DeFi protocol with 15+ interconnected contracts…”

Stop right there. If your protocol has 15+ interconnected contracts, you’ve already lost the security battle.

Every additional contract is:

  • Another attack surface
  • Another upgrade point
  • Another potential storage collision
  • Another governance risk

Simplicity IS security.

The “Move Fast” Fallacy

Dana justified upgradeability with “bug fixes that couldn’t wait for governance.”

This is the problem with modern DeFi: Ship fast, patch later.

Traditional software can do this because:

  • Users don’t lose money when you patch bugs
  • Patches can be tested in staging environments
  • Rollbacks are possible

DeFi patches control billions of dollars. The stakes are infinitely higher.

My Controversial Take

Most protocols using upgradeability don’t need it—they need better engineering.

If you:

  • Wrote better tests
  • Audited more thoroughly
  • Deployed to testnets with real incentives
  • Kept contracts simple

You wouldn’t need post-deployment upgrades.

Upgradeability is a crutch for inadequate pre-launch security.

Where Upgradeability Makes Sense

I’ll concede: L2 infrastructure needs upgradeability.

Optimism, Arbitrum, and other rollups are evolving technology. They NEED to upgrade sequencers, fraud proofs, etc.

But that’s different from application-layer DeFi.

Infrastructure ≠ Applications

The Real Question

Dana asked: “When is it safe to renounce upgrade ability?”

My answer: Before you deploy.

If you’re not confident enough to make your contract immutable, you’re not confident enough to handle other people’s money.

Final Thought

Upgradeability creates a false sense of security.

Developers think: “We can always patch bugs later.”

Then they ship faster, test less, and audit superficially.

The existence of upgradeability makes security worse, not better.

As someone who audits these contracts for a living, I have to inject some reality into this ideological debate.

The Developer Experience Is Terrifying

Brian said “just write better tests.” Let me tell you what that actually looks like:

Week 1: Write comprehensive unit tests. 100% coverage. Everything passes.

Week 2: Deploy to testnet. Discover that your mock ERC20 tokens behave differently than real ones (transfer returns false instead of reverting).

Week 3: Fix that. Deploy again. Discover that Chainlink oracles on testnet update every hour (vs every block on mainnet), breaking your liquidation logic.

Week 4: Fix that. Deploy again. Someone finds a cross-contract reentrancy bug that wasn’t caught because your tests don’t simulate complex multi-contract interactions.

Week 5: Fix that. Deploy to mainnet. Users discover that gas costs make your design economically unviable for small transactions.

This is NORMAL even for experienced developers.

The False Dichotomy

This debate is framed as: Immutable (secure) vs Upgradeable (insecure).

That’s wrong. The real spectrum is:

Immutable + Simple (Uniswap V2): Secure
Immutable + Complex (Parity wallet): Catastrophic failure
Upgradeable + Good governance (Aave): Secure enough
Upgradeable + Bad governance (countless rug pulls): Catastrophic

Simplicity matters more than immutability.

What I Wish Existed

Forget the immutable vs upgradeable debate. What we need:

1. Formal verification for upgrade logic

Not full protocol verification ($200K). Just verify:

  • Initialization can only be called once
  • Storage layout is compatible with previous version
  • Upgrade function has correct access control

Cost: $5K-$10K. Time: 1 week.

Every upgradeable protocol should do this.

2. Upgrade compatibility testing

GitHub Action that automatically:

  • Compares storage layouts between versions
  • Checks for dangerous delegatecalls
  • Validates proxy admin permissions

This should be as common as CI/CD.

3. Graduated immutability

Why can’t we have both?

Phase 1 (0-6 months): Upgradeable with 24h timelock
Phase 2 (6-18 months): Upgradeable with 7-day timelock
Phase 3 (18+ months): Immutable (renounce upgrade keys)

Flexibility when you need it, immutability when you’re confident.

Responding To Specific Points

Brian: “Uniswap V2 is immutable and works fine”

True. But Uniswap V2 does ONE thing. Most DeFi protocols need to:

  • Integrate with multiple oracles
  • Handle complex liquidation logic
  • Manage collateral types
  • Adjust risk parameters

These WILL need updates as markets evolve.

Sophia: “Formal verification is cheap for components”

Agree! This should be standard. But formal verification can’t prove:

  • Economic soundness (are liquidation thresholds set correctly?)
  • Oracle manipulation resistance
  • Governance attack resistance

Dana: “Progressive decentralization is the answer”

Yes, but HOW?

Most protocols say “we’ll decentralize later” and never do. Need concrete milestones:

  • Month 6: Add 3 independent multi-sig members
  • Month 12: Increase timelock to 48h
  • Month 18: Require token vote for upgrades
  • Month 24: Renounce if stable

My Practical Checklist

If you’re using upgradeable contracts, you MUST:

:white_check_mark: Use well-known pattern (UUPS, Transparent, Beacon—not custom)
:white_check_mark: Protect initializers with initializer modifier
:white_check_mark: Use storage gaps for future compatibility
:white_check_mark: Multi-sig the proxy admin (5+ signers, geographically distributed)
:white_check_mark: Add timelock (24h minimum, 7 days for high-TVL)
:white_check_mark: Test storage layout compatibility in CI/CD
:white_check_mark: Formally verify initialization and upgrade logic
:white_check_mark: Document who can upgrade and how
:white_check_mark: Monitor proxy admin transactions with real-time alerts

If you can’t check all these boxes, go immutable.

The Real Answer

Most projects shouldn’t use upgradeable contracts.

But for projects that DO need them (complex DeFi, L2 infrastructure), do it right or don’t do it at all.