DEV Community

Obinna Duru
Obinna Duru

Posted on

What the DAO Hack, Parity Freeze and Ronin Exploit Teach Us About Immutability

In 2016, an attacker discovered a bug in a smart contract called the DAO.

He didn't need to compromise Ethereum. He didn't need a zero-day exploit. He found one bad assumption in the withdrawal logic, wrote a contract that exploited it recursively, and drained $60 million. Every node on the Ethereum network confirmed every transaction. The blockchain did its job perfectly.

That's the thing about immutable code. It can't lie. But it also means it can't be fixed.

I've been sitting with this idea since I hosted Session 3 of BinnaDevLab Cohort 1. I invited Uchenna Ofoma, a software engineer of 3+ years currently focused on the distributed systems that underpin blockchain technology, to walk us through the full engineering reality of deploying code you can never take back. What followed was one of the most honest breakdowns of Web3 security I've heard.

If you'd rather watch than read, here's the full 42-minute session:

What "Deploying" Actually Means

I opened the session by framing the core question: what does it actually mean that a smart contract is immutable?

Most developers have an intuition for deployment -- you push code, it runs on a server, the server can be updated or rolled back. Uchenna immediately challenged that mental model.

When you deploy a smart contract, your code gets compiled to EVM bytecode -- an intermediate representation designed specifically to run on the Ethereum Virtual Machine. From that moment, your code gets a permanent address. Every node on the network holds a copy and can execute it. Nobody owns the server. There is no centralized server.

As Uchenna put it: "Once you deploy your smart contract, just take it that it's online forever or until the blockchain inevitably goes down someday, somehow."

The reason for this is deterministic execution. The EVM guarantees that given the same inputs, every node produces the same output. This is what makes decentralization possible: no one node needs to be trusted, because they all agree on the math. But it also means your bug is their bug permanently.

Two Kinds of Trust

Before Uchenna walked us through what goes wrong, he established something I thought was fundamental and it connects directly back to what we covered in Session 1.

"If the owner can go to edit the rules, you're back to trusting a company, which is what we're trying to avoid."

The whole promise of a smart contract is that you're replacing trust in a person with trust in code. Anyone can audit it. Anyone can verify it. It runs without asking permission. But the moment you give an admin the ability to change the code, you've reintroduced a human and with it, all of human failure.

That's the foundation:

"A contract you can edit is not an agreement -- it's a promise."

When Uchenna said that, I wrote it down immediately. It cuts straight to why any of this matters.

When Forever Goes Wrong

This is the section of the session I wasn't fully prepared for. I knew Uchenna was going to cover the major hacks. I didn't expect the specific engineering detail he brought to each one.

The DAO $60 million Hack

The DAO was a decentralised fund on Ethereum. Members could pool ETH and vote on how to allocate it. A smart contract managed all the funds.

There was a bug, a re-entrancy bug. Uchenna explained it in a way that made it immediately understandable:

The contract let you withdraw your balance. But before it updated your balance, it made an external call to send you the ETH. An attacker wrote a contract that, the moment it received ETH from the withdrawal, immediately called the withdrawal function again before the balance had been marked as zero. Then again. And again.

He kept entering the vault before it could record that he'd left.

By the end, he had drained approximately $60 million. The code didn't fail. No exception was thrown. The EVM executed every call exactly as instructed.

The fix for this pattern is called Checks-Effects-Interactions: update all your state variables before you make any external calls. Mark the balance as zero first. Then send the money. If someone tries to re-enter, they find an empty account.

The aftermath is one of the most significant moments in blockchain history. The Ethereum community forked the chain -- creating a copy up to just before the hack -- so that affected users could get their money back. The old chain, where the attacker kept his funds, became what we now know as Ethereum Classic. The blockchain literally split in two over one bug.

The Parity Wallet Freeze -- $280 million

In 2017, Parity Technologies built a popular multi-signature wallet. Multiple signatures were required to move funds, no one person could act alone.

The problem was architectural. Their wallets shared a single library contract. That library had an unguarded initialization function, no access control, no guard on who could call it.

A user exploring the code called it. Then, exploring further, called the function to delete the library.

$280 million was locked. Not stolen. Just permanently inaccessible, because the code that would authorise withdrawals no longer existed.

The fix is access control: every state-changing function needs to verify who is calling it before allowing the action to proceed.

The Ronin Sidechain Exploit -- $625 million

Ronin was the sidechain powering Axie Infinity. To validate transactions, it used nine validator keys. A majority, five of nine was required to approve any action.

Attackers compromised five of them.

Uchenna's analogy during the session stuck with me: "Half of nine is 4.5. They've compromised five. Five is more than 4.5."

With majority control, the attackers could authorize anything. $625 million left. The code worked exactly as designed. It was just the wrong people holding the keys.

How Engineers Live With It

After walking through what failed, Uchenna shifted to what engineers do in response. The honest answer is that there are no perfect solutions, only trade-offs.

  • Proxy Contracts: You can deploy a "proxy" that users interact with, which delegates logic to an implementation contract behind the scenes. When you need to patch a bug, you deploy a new implementation and point the proxy at it. The address never changes. The logic does.
  • Time Locks: Any upgrade or large fund movement gets queued with a delay like 72 hours. This gives the community time to notice if something suspicious is being pushed.
  • Multi-sig wallets: No single person can push the button. A 3-of-5 multi-sig requires three separate private keys to sign any transaction.
  • Third-party audits and bug bounties: Independent firms review your code. Or you open a bounty for anyone to report a vulnerability, get paid. The goal is to maximize the number of friendly eyes before deployment, because after deployment, every eye is potentially hostile.

Every Escape Hatch Is Also an Attack Hatch

This was the moment of the session that landed hardest.

Every mechanism you build to survive a bug is also a mechanism an attacker can use against you.

Proxy contracts? An attacker who controls the admin key can push a malicious implementation.

Time locks? The attacker now has 72 hours to prepare not just a few seconds.

Uchenna's framing:

"An immutable bug is honest. An upgradeable protocol is a promise with a delay."

An immutable contract is what it is. Its failure mode is fixed and visible. An upgradeable contract introduces ongoing risk: the humans holding the upgrade keys, forever. If those humans are ever compromised or ever act against users' interests, the upgrade path becomes the exploit.

The engineer's job, as Uchenna put it, is to decide which failure they can live with.

DYOR Is Not Just a Meme

I closed the session with something I feel strongly about, and I want to say it directly here too.

DYOR (Do Your Own Research), gets thrown around in crypto circles as a joke or a liability disclaimer. But after what Uchenna walked us through, it's the only honest answer to the immutability problem.

If you're building: audit your code, run open-source tools against it, get third-party reviews, and be honest about what your protocol assumes. Know what your upgrade path actually exposes.

If you're using: don't trust a protocol because the team told you to. Find the audit report. Look at who holds the keys. Understand whether the thing you're interacting with is truly immutable or whether there's an admin somewhere with a button.

The code can't lie. But you need to be able to read it.

We're running Cohort 1 of BinnaDevLab: a structured developer program built on reasoning from first principles. Session 3 was the theory. Session 4 is the practical: actually writing smart contracts and experiencing immutability firsthand.

Missed Session 1 on trust and decentralization? Watch it here.

Follow along on YouTube or join the community at binnadev.com

Top comments (0)