Wintermute Optimism Undeployed Safe Proxy Initialization Takeover (2022-06-05)

  • Name: Wintermute Optimism undeployed Safe proxy initialization takeover
  • Date: 2022-06-05
  • Protocol: Wintermute / Optimism / Gnosis Safe
  • Chain: Optimism
  • Category: unprotected initializer / delayed-deployment race / cross-chain replay deployment / custody takeover
  • Loss: 20,000,000 OP seized at the vulnerable address, worth roughly $27.6M at public disclosure; 17,000,000 OP were later returned, leaving roughly 3,000,000 OP permanently out of Wintermute / Optimism control
  • Summary: Wintermute gave Optimism a Safe address that already existed on Ethereum but had not been deployed on Optimism. That was survivable only if nobody else could recreate the same Safe proxy first. But Wintermute’s L1 Safe had been deployed in 2020 through an old Safe ProxyFactory.createProxy() path that used CREATE, so the future proxy address depended only on the factory address and factory nonce rather than CREATE2 salt binding. An attacker exploited that by replaying old pre-EIP-155 Safe deployer transactions onto Optimism to recreate the legacy ProxyFactory at the same address, mass-deploying thousands of Safe proxies until the nonce reached Wintermute’s L1 deployment slot, and initializing the matching Optimism proxy with an attacker-controlled owner set. Once that proxy existed at 0x4f3a120e72c76c22ae802d129f599bfdbc31cb81, the 20M OP sitting there stopped being Wintermute’s funds sent to the wrong network and became attacker-owned Safe assets.
  • Trigger / entry point:
    • Wintermute supplied Optimism with Safe address 0x4f3a120e72c76c22ae802d129f599bfdbc31cb81, which was a live Safe proxy on Ethereum but not yet a deployed contract on Optimism.
    • Optimism still sent test transfers and then the full OP allocation to that undeployed L2 address: 1 OP in 0xf79ed3037b55fbfd305007da2f19fb7960d31b8410453c679313e37a6d8548f4, 1,000,000 OP in 0x0c1d6166293924566ea0ca32d07379c7033a8b8f2558f667f917543e51dd474a, and 19,000,000 OP in 0x8e29eef359f6c18a06e229157d44467b5e873f6e5b996baa7124b38eb6dfb1db.
    • The original Safe had been created through legacy ProxyFactory.createProxy() code that used CREATE, so reproducing the same factory address and nonce sequence on Optimism would reproduce the same proxy address.
    • The Safe deployer transactions needed to recreate the old ProxyFactory address were replayable across chains because they predated EIP-155 chain-id signing.
  • Root cause:
    • Wintermute and Optimism treated an address as if it were already under Wintermute control on Optimism because it was under Wintermute control on Ethereum.
    • That assumption failed because contract-account identity is chain-local. A Safe address on L1 is not the same control surface as the same hex address on L2 until the proxy is actually deployed and initialized there.
    • Safe’s older CREATE-based factory path made the undeployed L2 address reproducible by anyone who could recreate the factory address and advance its nonce to the right point.
    • The decisive authorization failure was still initialization: the first party to create the matching proxy on Optimism could run setup() with its own owners and threshold, so the attacker turned not yet deployed into already mine.
  • Exploit path:
    1. Wintermute received 20M OP at an Optimism address that had no code deployed, even though the same hex address already corresponded to Wintermute’s Safe proxy on Ethereum.
    2. After Wintermute disclosed the mistake privately and began preparing a recovery, the attacker funded Optimism addresses 0x8BcFe4f1358E50A1db10025D731C8b3b17f04DBB and 0x60b28637879b5a09d21b68040020ffbf7dba5107, with public tracing showing Tornado Cash funding on the path.
    3. Using attacker wallet 0x60b28637879b5a09d21b68040020ffbf7dba5107, the attacker first sent gas to the legacy Safe deployer address 0x1aa7451dd11b8cb16ac089ed7fe05efa00100a6a in transactions 0xebe31b91705b2648ab4264fab02115782eafe26963ddc4ad99a2971d609ba376 and 0x90debe0ba3110b4760809783d9b6d2d03d14a11495d20f85ff60bded89449259.
    4. They then replayed old non-EIP-155 Safe deployer transactions on Optimism, recreating Gnosis Safe: Deployer 3 and using it to deploy the legacy Safe ProxyFactory to the same address 0x76e2cfc1f5fa8f6a5b3fc4c8f4788f0116861f9b used on Ethereum, with replay txs including 0x06d2fa464546e99d2147e1fc997ddb624cec9c8c5e25a050cc381ee8a384eed3, 0x31ae8a26075d0f18b81d3abe2ad8aeca8816c97aff87728f2b10af0241e9b3d4, and 0x75a42f240d229518979199f56cd7c82e4fc1f1a20ad9a4864c635354b4a34261.
    5. With the factory recreated, the attacker deployed auxiliary contract 0xe7145dd6287ae53326347f3a6694fcf2954bcd8a and then executed 62 batched createProxy() transactions, about 162 proxies per transaction, to walk the factory nonce forward until it matched the nonce that had created Wintermute’s Ethereum Safe.
    6. On the 55th batching attempt, transaction 0x00a3da68f0f6a69cb067f09c3f7e741a01636cbc27a84c603b468f65271d415b created a Safe proxy at 0x4f3a120e72c76c22ae802d129f599bfdbc31cb81 on Optimism.
    7. That newly created proxy was initialized with an attacker-controlled owner set and threshold before Wintermute completed its planned June 7 recovery with Safe support.
    8. Once the attacker-controlled Safe existed at the OP-holding address, they could execute ordinary Safe-authorized token transfers: 1,000,000 OP was sold for about 720 ETH, 1,000,000 OP was sent to Vitalik’s address, and the remaining 17,000,000 OP were later returned.
  • What the attacker needed to be true:
    • The vulnerable Optimism address had to be undeployed when it received the OP allocation.
    • The original Ethereum Safe needed to have been created through the older CREATE-based factory rather than a CREATE2 path that would bind deployment more tightly to initialization parameters.
    • The old Safe deployer / factory deployment transactions needed to be replayable on Optimism so the attacker could recreate the same factory address.
    • No competing recovery transaction from Wintermute and Safe could reach the matching proxy address and initialize it first.
    • The OP tokens had to remain at the undeployed address long enough for the attacker to reconstruct the factory and walk its nonce forward.
  • Technical anatomy worth remembering:
    • This was not an EOA private-key compromise. The attacker never stole Wintermute’s Ethereum Safe keys; they won the race to instantiate and initialize the same contract address on another chain.
    • The bug family is sharper than sent tokens to wrong network. The real failure was initialization rights were still unclaimed at a high-value address, and the address was reproducible by a public factory path.
    • The old Safe deployment architecture mattered twice: CREATE made proxy addresses nonce-derived rather than salt-bound, and pre-EIP-155 deployer transactions made the factory recreation step cross-chain replayable.
    • The replay detail is not abstract. Public reconstructions show the attacker could directly gas up the legacy deployer address on Optimism, replay the old factory-deployment transactions, and then brute-force the matching proxy nonce with 10,044 Safe creations across 62 batching transactions.
    • The exploit is a good reminder that custody recovery plans can themselves reveal the real attack path. Wintermute initially believed only it could reproduce the necessary deployment history; that assumption turned out to be the core security mistake.
    • Safe’s own public help guidance later framed this class clearly: an undeployed Safe address on another network is not safe just because the same address already hosts a Safe elsewhere.
  • Representative transactions and contracts:
    • Wintermute Safe on Ethereum / hijacked address on Optimism: 0x4f3a120e72c76c22ae802d129f599bfdbc31cb81
    • 1 OP test transfer: 0xf79ed3037b55fbfd305007da2f19fb7960d31b8410453c679313e37a6d8548f4
    • 1,000,000 OP test transfer: 0x0c1d6166293924566ea0ca32d07379c7033a8b8f2558f667f917543e51dd474a
    • 19,000,000 OP main transfer: 0x8e29eef359f6c18a06e229157d44467b5e873f6e5b996baa7124b38eb6dfb1db
    • Safe deployer on Ethereum and replayed address on Optimism: 0x1aa7451dd11b8cb16ac089ed7fe05efa00100a6a
    • Gas-funding txs to the replayed deployer on Optimism: 0xebe31b91705b2648ab4264fab02115782eafe26963ddc4ad99a2971d609ba376, 0x90debe0ba3110b4760809783d9b6d2d03d14a11495d20f85ff60bded89449259
    • Legacy Safe ProxyFactory on Ethereum and recreated Optimism address: 0x76e2cfc1f5fa8f6a5b3fc4c8f4788f0116861f9b
    • Replay-based Optimism factory-creation txs: 0x06d2fa464546e99d2147e1fc997ddb624cec9c8c5e25a050cc381ee8a384eed3, 0x31ae8a26075d0f18b81d3abe2ad8aeca8816c97aff87728f2b10af0241e9b3d4, 0x75a42f240d229518979199f56cd7c82e4fc1f1a20ad9a4864c635354b4a34261
    • Attacker helper contract deployment: 0x69ee67800307ef7cb30ffa42d9f052290e81b3df6d3b7c29303007e33cd1c240
    • Matching Safe creation / takeover tx: 0x00a3da68f0f6a69cb067f09c3f7e741a01636cbc27a84c603b468f65271d415b
    • Attacker wallets highlighted in public reconstructions: 0x8BcFe4f1358E50A1db10025D731C8b3b17f04DBB and 0x60b28637879b5a09d21b68040020ffbf7dba5107
    • 1,000,000 OP transfer to attacker EOA: 0x230e17117986f0dc7259db824de1d00c6cf455c925c0c8c6b89bf0b6756a7b7e
    • 1,000,000 OP sale transaction cited by rekt.news: 0xe9491bfb9a1ad13a47f3c1f61197b097416cbed2e32e038dd3de97172ddee303
    • 1,000,000 OP transfer to Vitalik: 0xdb693613d550e38d53d47b5fd07ce505e24e141db146fa1321710c9a86d9db6a
  • Response / mitigation:
    • Wintermute disclosed the error publicly on 2022-06-08, said it remained solvent, and committed to buy back any OP the attacker sold so market-making obligations would still be met.
    • Optimism fronted an additional 20M OP to Wintermute for its market-making program while governance fallout from the compromised allocation was assessed.
    • Wintermute, Optimism, and Safe reworked the recovery approach, but by then the attacker had already initialized the matching proxy.
    • The attacker later returned 17M OP, substantially reducing the financial damage but not changing the underlying custody lesson.

Pattern tags

  • unprotected-initializer-reinitialization
  • First-caller capture on Optimism did not require compromising Wintermute’s Safe owners; it only required finding a high-value address whose initialization ceremony had not happened yet on that chain
  • The decisive invariant break was same hex address being treated as same custody object across L1 and L2 even though factory history, deployment state, and setup() ownership were still chain-local
  • This is also a strong exemplar for implementation and singleton direct-call deauthorization’s narrower operational cousin: undeployed contract-wallet addresses should stay economically inert until the actual wallet instance exists and its owner set is fixed

Why the exploit worked

  • Wintermute and Optimism treated 0x4f3a...cb81 as if Wintermute already controlled it on Optimism because Wintermute controlled the same hex address on Ethereum. That compressed two different security objects — an L1 Safe proxy and an undeployed L2 address — into one story before the L2 wallet had any code or owner state at all.
  • The old Safe deployment path made that mistake monetizable. Because Wintermute’s original Safe had been created via legacy ProxyFactory.createProxy() with CREATE, the eventual proxy address depended on the factory address and factory nonce rather than on a CREATE2 salt bound to one specific initialization payload.
  • Public deployment history then supplied the second half of the exploit. The legacy Safe deployer transactions that recreated Gnosis Safe: Deployer 3 and the old ProxyFactory address were pre-EIP-155, so the attacker could replay them onto Optimism, recover the same factory address, and then advance its nonce until the matching proxy slot appeared.
  • Once that proxy was created at the OP-holding address, the exploit stopped looking like cross-chain replay weirdness and reverted to a very old initializer lesson: the first successful setup() call defines the owner set. Wintermute had not yet anchored that root of trust on Optimism, so the attacker could do it first and turn the funded address into an attacker-owned Safe.
  • The reusable lesson is sharper than wrong address on the wrong chain. Funds were sent to an address whose wallet identity had not been instantiated locally. In that state, deployment history, factory semantics, and initializer rights were still the real custody surface.

Detection and audit pivots

  • Treat contract-wallet addresses as chain-local objects, not reusable hex strings. Before funding or allowlisting a Safe / proxy / smart-account address on a new domain, prove that the contract is already deployed there, the initialization path is closed, and the expected owner set / threshold is visible on that chain.
  • Review wallet-factory determinism end to end: factory address reproducibility, CREATE versus CREATE2, deployer-transaction replayability, and whether nonce-walking can recreate a target address with a different initialization payload.
  • Search treasury and token-distribution flows for transfers to zero-code contract-wallet targets. Address is known on another chain is not an ownership proof if the destination address has no code yet.
  • Monitor for suspicious deployment activity at funded-but-undeployed addresses: unexpected factory recreation, mass proxy creation to walk nonces, first-time setup() / initialization events at treasury-relevant addresses, and sudden code appearance at addresses that already hold material balances.
  • Treat recovery plans that rely on privately reproducing public deployment history as suspect by default. If defenders can recreate an undeployed wallet address from public artifacts, attackers can usually study and race the same path too.

Why this incident matters

  • It is a very clean first-caller-initialization case where no protocol bug in the token itself was needed. The exploitable asset sat behind an unclaimed initialization ceremony.
  • It also preserves a rare cross-chain nuance: replayable deployer history and legacy CREATE-derived factory nonces can make an undeployed contract address reproducible on another EVM chain even when the original chain’s instance is secure.
  • The incident is therefore a useful bridge between classic initializer bugs like Parity and modern operational mistakes in multisig custody. The authorization boundary was not who has the keys; it was who gets to instantiate the wallet here first.

Failure-prevention checklist

  • Do not fund or allowlist a contract-wallet destination on a new chain until the wallet is already deployed there, the expected bytecode hash is present, and the owner set / threshold / module state is machine-checked on that chain.
  • Treat same hex address on another chain as a naming convenience, not as custody proof. Treasury, distribution, and bridge operations should require explicit destination-chain deployment-state checks before release.
  • Prefer deployment rails that bind address creation to the intended initialization payload, such as CREATE2-style deterministic deployment with reviewed init data, rather than legacy nonce-walkable CREATE paths that let third parties recreate the same address and choose the first setup().
  • Collapse deployment and initialization into one atomic ceremony for funded smart accounts whenever possible. If the wallet cannot be created and initialized before funding, the address should stay economically inert.
  • Inventory old deployer accounts, factory contracts, and replayable deployment artifacts that can still recreate privileged addresses on new chains or domains. A retired deployment path is still live authority if balances or governance rights will trust it later.
  • Alert on material balances arriving at zero-code smart-account destinations, then block follow-on transfers until the local contract instance and initialization state are verified.
  • Treat recovery plans based on replaying public deployment history as race conditions, not private rescue channels. If defenders can reproduce the path from public artifacts, attackers can usually study and race it too.
  • Keep distribution and custody playbooks explicit about chain-local trust anchors: deployed, initialized, and owned by expected signers on this chain should be separate gates, not assumptions inferred from another domain.

Audit prompts

  • Which wallet, proxy, or account-abstraction addresses are reused socially across chains, and what exact checks prove those destinations are already deployed and initialized on each chain where funds may be sent?
  • Can any privileged address still be recreated by replaying old deployer transactions, reproducing a factory at the same address, or walking a factory nonce with legacy CREATE semantics?
  • Does the deployment path bind the future address to one reviewed initialization payload, or could a third party create the same address and run setup() / initialize() first with a different owner set?
  • Are there pre-EIP-155 or otherwise replayable deployment transactions in the project’s bootstrap history that remain relevant for future wallet recreation on other EVM domains?
  • What operational guard stops treasury, grant, or token-distribution systems from sending large balances to zero-code contract-wallet addresses?
  • Can defenders deploy and initialize destination wallets atomically before funds move, or does the process leave a public gap where first-caller authority is still unclaimed?
  • Which legacy deployer EOAs, factories, or migration scripts are still trusted implicitly even though the organization thinks those rollout paths are long retired?

Lessons that should already be standard

  • A smart-account address is not a custody object until that chain’s instance actually exists and its initialization ceremony is over.
  • First-caller initialization rights are real root authority. If outsiders can instantiate the wallet first, they own the account no matter who controlled the same address elsewhere.
  • Cross-chain operational reuse of an address string is dangerous when deployment semantics differ. It is our Safe on Ethereum says nothing by itself about who controls the same hex address on Optimism.
  • Legacy deployment tooling can stay security-critical long after teams think they have moved on. Old factories, deployer EOAs, and replayable bootstrap transactions are still part of the threat model if future funds or governance assumptions depend on reproducing an address.
  • Funding before deployment is not a harmless convenience for contract wallets. It turns an address-generation detail into an open race for account ownership.
  • Recovery playbooks must assume public reproducibility. A rescue path that depends on recreating a wallet from known artifacts is usually an attack path too.

Internal linkages

Control surface