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.6Mat 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 usedCREATE, so the future proxy address depended only on the factory address and factory nonce rather thanCREATE2salt 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 at0x4f3a120e72c76c22ae802d129f599bfdbc31cb81, the 20M OP sitting there stopped beingWintermute’s funds sent to the wrong networkand becameattacker-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 OPin0xf79ed3037b55fbfd305007da2f19fb7960d31b8410453c679313e37a6d8548f4,1,000,000 OPin0x0c1d6166293924566ea0ca32d07379c7033a8b8f2558f667f917543e51dd474a, and19,000,000 OPin0x8e29eef359f6c18a06e229157d44467b5e873f6e5b996baa7124b38eb6dfb1db. - The original Safe had been created through legacy
ProxyFactory.createProxy()code that usedCREATE, 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.
- Wintermute supplied Optimism with Safe address
- 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 turnednot yet deployedintoalready mine.
- Exploit path:
- 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.
- After Wintermute disclosed the mistake privately and began preparing a recovery, the attacker funded Optimism addresses
0x8BcFe4f1358E50A1db10025D731C8b3b17f04DBBand0x60b28637879b5a09d21b68040020ffbf7dba5107, with public tracing showing Tornado Cash funding on the path. - Using attacker wallet
0x60b28637879b5a09d21b68040020ffbf7dba5107, the attacker first sent gas to the legacy Safe deployer address0x1aa7451dd11b8cb16ac089ed7fe05efa00100a6ain transactions0xebe31b91705b2648ab4264fab02115782eafe26963ddc4ad99a2971d609ba376and0x90debe0ba3110b4760809783d9b6d2d03d14a11495d20f85ff60bded89449259. - They then replayed old non-EIP-155 Safe deployer transactions on Optimism, recreating
Gnosis Safe: Deployer 3and using it to deploy the legacy SafeProxyFactoryto the same address0x76e2cfc1f5fa8f6a5b3fc4c8f4788f0116861f9bused on Ethereum, with replay txs including0x06d2fa464546e99d2147e1fc997ddb624cec9c8c5e25a050cc381ee8a384eed3,0x31ae8a26075d0f18b81d3abe2ad8aeca8816c97aff87728f2b10af0241e9b3d4, and0x75a42f240d229518979199f56cd7c82e4fc1f1a20ad9a4864c635354b4a34261. - With the factory recreated, the attacker deployed auxiliary contract
0xe7145dd6287ae53326347f3a6694fcf2954bcd8aand then executed 62 batchedcreateProxy()transactions, about 162 proxies per transaction, to walk the factory nonce forward until it matched the nonce that had created Wintermute’s Ethereum Safe. - On the 55th batching attempt, transaction
0x00a3da68f0f6a69cb067f09c3f7e741a01636cbc27a84c603b468f65271d415bcreated a Safe proxy at0x4f3a120e72c76c22ae802d129f599bfdbc31cb81on Optimism. - 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.
- Once the attacker-controlled Safe existed at the OP-holding address, they could execute ordinary Safe-authorized token transfers:
1,000,000 OPwas sold for about720 ETH,1,000,000 OPwas 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 aCREATE2path 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 wasinitialization 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:
CREATEmade 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 OPtest transfer:0xf79ed3037b55fbfd305007da2f19fb7960d31b8410453c679313e37a6d8548f41,000,000 OPtest transfer:0x0c1d6166293924566ea0ca32d07379c7033a8b8f2558f667f917543e51dd474a19,000,000 OPmain 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:
0x8BcFe4f1358E50A1db10025D731C8b3b17f04DBBand0x60b28637879b5a09d21b68040020ffbf7dba5107 1,000,000 OPtransfer to attacker EOA:0x230e17117986f0dc7259db824de1d00c6cf455c925c0c8c6b89bf0b6756a7b7e1,000,000 OPsale transaction cited by rekt.news:0xe9491bfb9a1ad13a47f3c1f61197b097416cbed2e32e038dd3de97172ddee3031,000,000 OPtransfer to Vitalik:0xdb693613d550e38d53d47b5fd07ce505e24e141db146fa1321710c9a86d9db6a
- Wintermute Safe on Ethereum / hijacked address on Optimism:
- 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 addressbeing treated assame custody objectacross L1 and L2 even though factory history, deployment state, andsetup()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...cb81as 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()withCREATE, the eventual proxy address depended on the factory address and factory nonce rather than on aCREATE2salt 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 3and the oldProxyFactoryaddress 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 weirdnessand reverted to a very old initializer lesson: the first successfulsetup()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,
CREATEversusCREATE2, 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 chainis 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 waswho 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 chainas 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-walkableCREATEpaths that let third parties recreate the same address and choose the firstsetup(). - 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, andowned by expected signers on this chainshould 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
CREATEsemantics? - 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 Ethereumsays 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
- Core recurring pattern: unprotected-initializer-reinitialization.
- Positive-design companion: implementation-and-singleton-direct-call-deauthorization — the healthier operational version of this lesson is that undeployed or shared code paths should be directly harmless until the real trust anchor has been established on the destination chain.
- Positive-design companion: decommissioning-authority-extinguishment-discipline — useful when the sharper question becomes how legacy deployer rails, bootstrap assumptions, or rollout-only address expectations should have been extinguished before live funds depended on them.
- Adjacent setup / storage family: storage-layout-collision.
- Closest older canonical case: parity-multisig-wallet-initwallet-ownership-reset-2017-07-19.
Control surface
-
The leverage sat in chain-specific contract deployment state, Safe factory address reproducibility, factory nonce sequencing, and the first successful
setup()call on the Optimism proxy. -
Wintermute did not mainly fail because the address string was wrong. It failed because contract-wallet control had not yet been established on the destination chain, and initialization rights were still publicly contestable.
-
Sources:
- https://gov.optimism.io/t/message-to-optimism-community-from-wintermute/2595
- https://rekt.news/wintermute-rekt
- https://inspexco.medium.com/how-20-million-op-was-stolen-from-the-multisig-wallet-not-yet-owned-by-wintermute-3f6c75db740a
- https://optimistic.etherscan.io/tx/0x00a3da68f0f6a69cb067f09c3f7e741a01636cbc27a84c603b468f65271d415b
- https://etherscan.io/tx/0xd705178d68551a6a6f65ca74363264b32150857a26dd62c27f3f96b8ec69ca01
-
Last reviewed: 2026-07-19 UTC