☠ Case Studies
← All Case Studies

Parity Multi-Sig Wallet Freeze

💸 $150,000,000 frozen 🔒 Uninitialized Library + Accidental Self-Destruct 📅 2017-11-01 00:00:00 +0000

An uninitialized library contract was claimed by a user who then accidentally destroyed it, permanently freezing 513,000 ETH held by ~587 wallets.

Background

Parity Technologies built a popular multi-signature wallet for Ethereum. Their architecture used a delegatecall pattern: a shared library contract held all the logic, and individual wallet contracts delegatecall'd into it to execute. This was elegant — one codebase, many wallets.

The library contract was deployed on-chain but never properly initialized.

The Vulnerability

The library had an initWallet() function that anyone could call — there was no check that it had already been initialized. This function set the contract's owner list. Since it was never called during deployment, it was open for anyone to claim.

🚨 Vulnerable Library Code
// No initialization check — anyone can call this function initWallet( address[] _owners, uint _required, uint _daylimit ) { initDaylimit(_daylimit); // Sets owners array — no check if already initialized! initMultiowned(_owners, _required); } // Only "owner" can call this function kill(address _to) onlyowner { selfdestruct(_to); // Destroys the library forever }

The Attack — Step by Step

  1. A GitHub user noticed the uninitialized library contract
  2. They called initWallet(["their_address"], 1, 0) — making themselves the sole owner
  3. Then called kill(their_address) — which executed selfdestruct on the library
  4. The library contract ceased to exist on-chain
  5. All ~587 wallet contracts that delegatecall'd into the library now pointed to a non-existent address
  6. Every transaction to those wallets failed. ~513,000 ETH ($150M) was permanently frozen

The user claimed it was accidental. Whether intentional or not, the result was the same: irreversible loss.

Impact

Unlike the DAO hack, there was no hard fork to recover these funds. The ETH is still frozen in those wallets to this day. This remains one of the largest permanent losses in Ethereum history from a smart contract bug.

Lessons Learned

Key Takeaways

Always initialize contracts immediately after deployment. Use initializer modifiers (as in OpenZeppelin's Initializable pattern) to prevent double-initialization and unauthorized initialization.

  • Initialize immediately: Call the initialization function in the same deployment transaction
  • Guard initialization: Use a bool initialized flag or OpenZeppelin's Initializable
  • Think twice about selfdestruct: Does your contract really need it? The risk often outweighs the benefit
  • Audit library contracts: Shared logic contracts deserve extra scrutiny — one bug affects every dependent contract