October 20, 2026
|
Exploit Postmortem
Cover image for "Startale's $2.9K Loss and How Olympix Would Have Prevented It"

Startale's $2.9K Loss and How Olympix Would Have Prevented It

Date: September 16, 2026

Chain: Ethereum

Loss: about $2,876 in USDC and USDT across 330 pre-funded accounts, per ExVul

Class: one-time initialization guarded by a transient flag that stayed set for the rest of the transaction

At 04:00:23 UTC on September 16, 2026, a single Ethereum transaction swept small balances, between 0.2 and 12.42 USDC each, out of 22 smart accounts that had never been used. Their owners had done what account abstraction invites people to do: they funded their wallet's address before the wallet contract existed, expecting it to be theirs the moment it was deployed.

According to ExVul's alert, that transaction was one of 41 batches that together took about $2,876 in USDC and USDT from 330 Startale ERC-7579 accounts. Each account was deployed and set up correctly, and then set up a second time by someone else before the transaction ended, because the only thing standing between initializeAccount and a stranger was a flag held in transient storage, which doesn't clear until the whole transaction is over.

Wallets That Exist Before They're Deployed

Startale ships a modular smart account built on ERC-7579, a standard for smart contract wallets that can plug in validators, executors and other modules. Like most account abstraction wallets, a Startale account has a counterfactual address: the factory deploys it with CREATE2, so the address is known in advance, and users can receive funds there before the contract is deployed. Deployment happens later, through StartaleAccountFactory.createAccount().

When the factory deploys an account, the account has to be initialized exactly once, with the owner's validator installed so only the owner can authorize anything. Whoever controls that first initialization controls the account, which makes "exactly once" the rule everything else rests on.

A Flag That Outlives the Moment It Guards

According to the DeFiHackLabs reproduction, StartaleSmartAccount used EIP-1153 transient storage as its initialization gate. Transient storage is a newer kind of contract memory that's cheaper than regular storage and is wiped automatically at the end of each transaction. The AccountProxy's constructor set the transient flag, then delegatecalled the legitimate initializeAccount to install the owner's validator. There was no persistent "already initialized" marker, so the transient flag was the only check.

The flaw is in when that flag goes away. Transient storage is cleared at the end of the transaction, and nothing cleared it earlier. After createAccount() finished deploying and initializing an account, the flag stayed set on that account for every remaining step of the same transaction, so a second call to initializeAccount found the gate still open.

A guard is only as good as its lifetime. A one-time setup check has to stay closed for the life of the contract, and a flag that lives for one transaction closes too late for any code that runs later in that transaction. It's like a hotel key card that's meant to work once for check-in but keeps opening the room until the front desk closes for the night.

Forty-One Batches From One Pattern

The PR describes the attack. In the same transaction as the deployment, an unrelated contract called initializeAccount(evilInitData) on the freshly deployed account, passed the transient check, and delegatecalled into a bootstrap the attacker supplied, which swept the account's pre-funded balance. It needed no signature, no privileged caller and no attacker capital. SlowMist's incident database records the drain of about 330 pre-funded counterfactual accounts on Ethereum and notes that the Soneium network wasn't affected.

In the example transaction, sent from 0x901DafdE7057BC2478d1eF640fb5515EA4757AAB in block 25987506, the attacker deployed a contract that created 42 contracts in total, collected 22 USDC transfers from victim accounts, and forwarded 213.78 USDC to the attacker's address. Etherscan shows that address receiving USDC from four attack contracts between 04:00 and 04:57 UTC, 759.34 USDC in total (213.78, 385.32, 2.11 and 158.14), and sending all of it on to LI.FI's LiFi Diamond contract in three transfers. ExVul's alert names a second attacker address, 0x9113...8b5b. The DeFiHackLabs PoC forks the chain at block 25987505, reproduces three of the victim accounts through the real factory and account functions, and asserts a swept total of 32.555561 USDC.

How Olympix Tests a Rule That Must Hold Exactly Once

Olympix reads a contract the way an attacker does, as states, transitions and entry points, and it uses the context around the code, here the account lifecycle of counterfactual deployment followed by initialization, to infer the rules the system relies on. The state that matters is who controls the account's validators, and initializeAccount is the entry point that sets it. The signal Olympix's static analysis surfaces is a transient storage value set during initialization and never cleared, sitting as the only guard on a function that hands over control of the account. Olympix has flagged this class before in other codebases.

BugPoCer, Olympix's Counterexample-Guided Invariant Refutation tool, looks at the account as a whole lifecycle, from counterfactual address to deployed wallet. The rule it pulls out of that lifecycle is short: an account gets initialized once, by the deployment that creates it, and never again. BugPoCer then runs every path it can reach against that rule, and the one that breaks it barely needs exploring, since it fits inside a single transaction: deploy the account through the factory, then call initializeAccount a second time with a bootstrap of the caller's choosing.

The counterexample shows the second initializeAccount call succeeding with attacker-supplied data and the pre-funded balance leaving the account, with the minimal conditions that trigger it and a generated Forge test that replays it. That's the September 16 drain, produced in development instead of on mainnet. Olympix runs at every merge and before release, so a change that moves an initialization guard from regular storage to transient storage gets re-tested against the same invariant before it ships.

Cheaper Storage, Same Rule

The accounts Startale's users funded early were exactly the ones the counterfactual model is built for, and they were the ones exposed. Transient storage is a useful tool for locks that should vanish when a transaction ends, and a one-time initialization gate needs to outlast the transaction that sets it. If your contracts use transient storage anywhere near access control, book a technical walkthrough.

On-Chain References

Protocol: Startale ERC-7579 smart accounts

Chain: Ethereum

Date: September 16, 2026, 04:00:23 UTC (example batch)

Block: 25987506

Loss: about $2,876 in USDC and USDT across 330 accounts and 41 batches, per ExVul's alert

Attacker EOA: 0x901D...7AAB

Attack contract (example batch): 0xe271...EcdE

StartaleSmartAccount: 0x0000...e05a

StartaleAccountFactory: 0x0000...ef90

Example exploit tx (1 of 41): 0x1a02...36a2

Sources

DeFiHackLabs PR #1270: Startale ERC-7579 transient-storage re-init drain PoC

ExVul: Startale exploit alert

SlowMist Hacked: Incident database (Startale, 2026-09-16)

EIP-1153: Transient Storage Opcodes

More from Olympix:

No items found.

Ready to Shift Security Assurance In-House? Talk to Our Security Experts Today.