MetaSea's $81.1K Loss and How Olympix Would Have Prevented It
In block 463,840,994 on Arbitrum, at 17:19:35 UTC on May 17, 2026, a freshly deployed contract opened a position in a MetaSea round with 3,000 USDT, redeemed it moments later, and walked away with 17,101,229.89 SEA from the protocol's reward distributor and 2,000 USDT of its own money back. It did that twelve times inside the same transaction, and the transaction was one of five that paid its owner a combined 81,130.86 USDT within a few dozen blocks.
MetaSea paid SEA yield on positions in its rounds, funded from a shared distributor. According to anomly's analysis of the trace, the payout on redeemPosition() was treated as claimable immediately and sized from the distributor's accumulated balance, and nothing stopped a position from being opened and redeemed in the same block. A reward meant to grow with time spent in a round could be collected with no time spent at all.
Rounds, a Distributor and a Reward That Didn't Check the Clock
The moving parts are visible in the transfers. A user opened a position in the round contract at 0xa70f...37C5 by paying USDT. In the focal transaction, each 3,000 USDT opening sent 1,000 USDT into the SEA/USDT pool at 0xeeb9...0513, where it bought SEA that was sent to the dead address, and anomly reports the other 2,000 USDT going to the distributor's accumulator. SEA for rewards sat in a distributor contract at 0x5210...220e, which also collected a share of SEA from trades in that pool.
On redemption, the distributor sent SEA to the round, and the round passed it to the redeemer along with a 2,000 USDT cash leg. Anomly measured each redeem pulling between about 14 million and 27 million SEA, against roughly 1.2 million SEA contributed per call. Both the round and the distributor are unverified on Arbiscan, so their code can't be read from an explorer, and anomly describes its account of the logic as a reconstruction from the trace.
Reward systems that pay for participation usually measure it somewhere, whether as a lockup, a last-claim timestamp or a block-number check, so that a position has to exist for a while before it earns. Anomly found none of those on MetaSea's redeem path. Without one, the distributor's balance behaved like a prize for showing up, and anyone who could show up and leave in the same block could claim it repeatedly.
Twelve Cycles on 3,300 Borrowed USDT
The attacker's wallet, 0x3521...3674, called a factory contract that deployed a new attack contract for each transaction. In the focal one, that contract borrowed 3,300 USDT0 from Aave V3 in a flash loan, which turned out to be all the capital the attack needed.
Each cycle followed the same pattern. The contract spent 300 USDT buying SEA, opened a position with 3,000 USDT, checked its round state, and called redeemPosition(), collecting the distributor's SEA payout and the 2,000 USDT cash leg. It then sold the SEA back into the pool for USDT and started again. Anomly reports the per-redeem payout climbing over the run, from about 17.10 million SEA to about 27.69 million SEA. At the end, the contract repaid Aave 3,301.65 USDT0, including a 1.65 USDT0 premium, and forwarded 13,904.94 USDT0 to the attacker's wallet.
Arbiscan shows five of the attacker's transactions paying out, starting in block 463,840,948, for 17,613.85, 16,978.19, 16,557.05, 16,076.83 and 13,904.94 USDT0. The next attempt, nine seconds after the focal transaction, reverted without moving any tokens. Anomly reports that MetaSea's admin address called pause() on the round contract in block 463,844,859, about 80 seconds after the attacker's last transaction.
Where the 81,130 USDT Went
Later, in block 463,938,531, the attacker's wallet swapped exactly 81,130.86 USDT0 for 38.24 WETH. Within the next several thousand blocks it sent 1, 15 and 10 WETH to the Relay bridge's depository and 5 WETH through Relay's router, and about nine days later it moved roughly 7.5 WETH more through the Across bridge. The 81,130.86 USDT0 figure is what the attacker took out in stablecoins. Anomly's estimate of about $110K counts the campaign more broadly and notes that the loss measured in SEA removed from the distributor runs higher than the USDT skimmed. As of November 6, 2026, we hadn't found a public statement from MetaSea about the incident beyond the on-chain pause.
How Olympix Would Have Found a Reward With No Waiting Period
Olympix reads a contract as an attacker would, as states, transitions and entry points, and it takes in the surrounding context, including docs and naming, to infer the invariants a system depends on. In a staking or rounds design, the states that matter are a position's start, its accrued reward and the distributor's balance, and the transitions are opening, accruing and redeeming. Olympix works on the code a team ships, so the fact that MetaSea's contracts were never verified on Arbiscan wouldn't have kept them out of reach.
The engine maps paths and uses signals to narrow from all of them to the feasible ones, and then to the ones that touch an invariant. Olympix's static analysis would have surfaced the signal on this redeem path: a function that transfers rewards out of a shared distributor with no read of a position's start time, block number or last claim anywhere on the way. A payout path that never consults the clock, in a system whose rewards are supposed to depend on time, is exactly what pushes a path to the front of the search.
The invariant takes one sentence: a position should earn rewards in proportion to the time and capital it actually committed, so one opened and closed in the same block should earn close to nothing. BugPoCer would have run Counterexample-Guided Invariant Refutation from the system outward, taking that rule as given and executing open and redeem sequences across the round and distributor until one broke it. The very first open-then-redeem in a single block would have done it, and repeating the pair would have shown the distributor draining faster with each cycle.
That would have produced a counterexample: open a position, redeem it in the same block, sell the SEA, repeat. BugPoCer would have returned the sequence with its minimal conditions and a generated Forge test that replays it, which is the May 17 campaign produced in development. Run at every merge and before release, that test would have pointed to the fix while it was still a single condition on redeemPosition().
Rewards Need a Clock
MetaSea's rounds had all the parts of a yield product except the one that makes yield mean something, a requirement that a position exist for some time before it earns. Whenever a payout is sized from a pool everyone shares, the code deciding who can claim it, and when, carries the whole system's solvency. To see how Olympix tests your reward and redemption paths against the invariants your design depends on, book a technical walkthrough.
On-Chain References
Protocol: MetaSea (SEA token rounds and reward distributor)
Chain: Arbitrum
Date: May 17, 2026, focal tx at 17:19:35 UTC
Loss: 81,130.86 USDT0 to the attacker across five transactions (on-chain)
Focal exploit tx: 0x001c...7716
First exploit tx: 0x0d9f...6dcf
Other exploit txs: 0xb20b...0280, 0x021c...75ca, 0x2ee5...935a
Attacker EOA: 0x3521...3674
Attack factory: 0x7b1d...69B7
Round contract (unverified): 0xa70f...37C5
SEA distributor (unverified): 0x5210...220e
SEA/USDT pool: 0xeeb9...0513
USDT0 to WETH swap tx: 0x583f...c7c6
More post-mortems
See every post-mortem →See what Olympix finds on your own code
Send us a repo that's already been audited, and we'll run Olympix at the same commit and show you what comes back.




