On the morning of April 30, 2026 (UTC), an attacker drained Wasabi Protocol's perpetuals vaults on Ethereum and Base, and later on Berachain and Blast. The first reports put the loss at about $4.5M, later $5.9M across four chains, with roughly $2M on Ethereum and $2.5M on Base12.
There was no bug in the vault math. One key, the deployer's, was the only admin. Whoever held it could grant roles and upgrade the contracts. This post asks when the chain first showed something was wrong, and how much was still in the vaults at that moment.
How Wasabi was administered
Wasabi's perpetuals contracts are UUPS proxies managed by a PerpManager contract with OpenZeppelin AccessControl. The ADMIN_ROLE on the PerpManager can upgrade the vaults and pools12.
- Ethereum PerpManager:
0xc0b0…7e7d - Base PerpManager:
0xec3e…d70e - Deployer, which held the admin role:
0x5c62…c0b8
One externally owned address with full admin rights and no timelock means one leaked key is enough. Hypernative called it exactly that: "the deployer key is the only admin"2.
The attack, step by step
All times UTC. We read the block time of every transaction below over public RPC3. The labels of each step come from Rekt1.
07:47:59 — Ethereum: ADMIN_ROLE granted to a fresh contract
The deployer called grantRole on the Ethereum PerpManager and gave ADMIN_ROLE to 0x878E…F3bF, a contract deployed for the attack (0x11ff…1ded).
This is the first on-chain signal. A new admin on a contract that holds user funds, granted to an address with no history, is never routine.
07:49:47 — Ethereum: 8 vaults in one transaction
108 seconds after the grant, one transaction emptied eight Ethereum vaults (0xcd77…0116). Rekt describes the upgrades of the vault proxies to attacker code as part of this step1. We did not find the upgrade transactions separately, so we do not give their times.
07:51:11 — Base: the same grant
The same pattern on Base: grantRole on the Base PerpManager (0x0b5d…52b7). A Base rule on the same event would have fired a minute or two later. The Ethereum alert, already on its way, was the earlier warning for Base.
07:52:37 — Base: vaults drained
86 seconds after the Base grant (0x10b3…7e40).
07:57:47 to 08:15:57 — the second round
The attacker came back. A second grant on Base at 07:57:47 (0x1855…67a8), a second grant on Ethereum at 07:58:59 (0x985b…fac1), the Ethereum LongPool drained at 07:59:35 (0xbabb…c317), a third Base grant at 08:12:47 (0xa2ec…ce2f) and a second Base drain at 08:15:57 (0x6b18…9626).
From the first grant to the last drain: 28 minutes. An alert at about 07:51 left 8 minutes before the LongPool and 25 minutes before the second Base drain.
What no rule would have changed
How to set up these rules in SmartAlert
- Add your access-control contract — the one with
AccessControlorOwnable— not just the vaults. - Create a Role granted rule, any event, Critical. It fires on
RoleGrantedandRoleRevoked. - On every proxy, create a Proxy upgraded rule. SmartAlert offers it automatically for ERC-1967 proxies.
- On each vault, add a transfer above an amount in $ rule with
from= the vault, for the drain itself. - Repeat on every chain you are deployed on. The attacker went chain by chain. The first chain's alert is the warning for the others.
- Send Critical alerts to people who hold a pause key. Telegram is free.
See how to monitor an upgradeable proxy and the post-audit checklist.
Sources
- 1.Rekt, Wasabi Protocol — Rekt: admin grant, UUPS upgrades, drains, loss across four chains. ↩
- 2.Hypernative, When the deployer key is the only admin: loss split by chain, admin structure. ↩
- 3.Checked by SmartAlert on-chain: block times of every transaction in this post, over public Ethereum and Base RPC. ↩
- 4.SmartAlert, alert delay on Ethereum: 12 confirmations (about 144 s), head polling up to 12 s, storage up to 15 s (P95), rule check every 60 s on Pro. ↩