On-chain investigationEthereumGnosis Safe15 September 2026

How a Safe module drained 2,900 rsETH in one block

At 04:38 UTC a searcher got into an Ethereum block first. By the time anyone else landed a transaction, 2,900 aEthrsETH had left a Gnosis Safe without an owner signature.

At a glance
A Gnosis Safe is a multisig wallet that can also turn on modules: contracts allowed to move funds without a fresh owner signature. On 15 September one of those paths on Ethereum was public. A batch contract called itself, drove a module the Safe had already turned on, and 2,900 aEthrsETH left in one capture tx. A searcher known as Yoink took the rsETH in-block. 2,882 rsETH still sits at one wallet. Later that morning Yoink seized another 157.71 rsETH by covering WETH debt on Aave, and copycats repeated the recipe.
2,882
rsETH still sitting at one wallet
$7.8M
That pile, at the capture print
0
rsETH out of that wallet since
175
Calls in the capture tx, all succeeded

01 · One block, no stolen key

Two enabled modules and a public batch path

At 04:38 UTC a bot called yoink() and took the first slot in the block. The attacker who set the trap has no tx of their own in it.

A Gnosis Safe is a multisig wallet. Owners sign, and the Safe runs the call. It can also turn on a module: a contract allowed to move funds without a fresh owner signature. Keepers, vaults and LP bots use that. Anyone who can reach the module can move funds the same way.

This was not a stolen key. It was a module the Safe had already turned on.

The victim wallet is a Safe 1.3.0 proxy from autumn 2023. For months it held Kelp rsETH on Aave V3 as aEthrsETH, with WETH debt against it. Two modules had been calling it since: a gateway from that first autumn, and a Uniswap v4 LP module from early this year. Keepers already used a public batch contract to reach them. The attacker reused that path.

Bitquery indexes every call, event and transfer on Ethereum. We pulled the capture tx, every aEthrsETH outflow from the Safe that day, and the wallets the rsETH landed in. Decoding those module execs is what the smart contract API is for. Following the aToken out is what the address APIs sit on.

02 · The batch contract that named itself

A target check that trusted address(this)

A public executor sits in front of the modules. It exposes a batch call: the caller picks a target and a list of payloads. A check on the target is supposed to keep others out. In the capture tx the helper passed the executor as the target, so the executor called itself.

A "target must be on the allowed list" check treats the contract's own address as trusted. Inner payloads then ran as that trusted executor, through the enabled module, inside the Safe.

The inner payload was another batch call, this time aimed at the gateway module. That module is allowed to call the Safe's module exec. It did so with op set to delegate call. Execute then ran in the Safe's own context and handed work to the Uniswap v4 LP module, which is itself an enabled module.

Keepers had already used this recipe. One of them called the batch function dozens of times. The path was live long before this morning.

The Safe processed every module call as designed. The hole was the caller list on the public batch contract.

03 · A junk token and a vanilla pool

PAT, then Uniswap v4, then an unlocker

Two blocks before the capture, the attacker put a token on chain called PAT and minted a huge pile of it to a helper. The next block, the helper seeded a Uniswap v4 pool of aEthrsETH against PAT. The pool key has hooks set to the zero address. This is a plain v4 pool. We mapped the hooked version on Robinhood Chain.

The Safe then approved the aToken and one wei of PAT through Permit2, and minted an LP NFT. The aToken moved from the Safe into the Pool Manager. A separate contract unlocked the pool, swapped, and took the aToken to the helper. The helper paid PAT as the other side of that swap. PAT has no market, so paying it is how the aToken leaves.

Public notes called that unlocker a hook. The call trace shows an ordinary call into Pool Manager, the same unlock any v4 router can make. The Safe still holds the NFT, empty of value.

The three minutes before Yoink15 September 2026, UTC
04:38:23
PAT is born
The attacker deploys a helper that creates PAT and the unlocker, then mints a huge PAT supply to itself.
04:38:35
The pool exists
The helper seeds Uniswap v4 Pool Manager with dust PAT. The pool is live before the capture tx.
04:38:47 idx 0
Yoink goes first
A Yoink EOA calls yoink() on its bot, 999 wei. The original attacker has no tx in this block.
same tx
The Safe is used as deputy
Helper → executor batch(self) → module exec → delegate call execute → LP module mints the PAT pool.
same tx
The aToken leaves
Unlocker swaps and take()s aEthrsETH to the helper. Helper withdraws on Aave. rsETH goes to the Yoink bot.
same tx
The split
2,882.37 rsETH to a reused Yoink wallet. 17.63 rsETH sold on a real ETH/rsETH v4 pool for 18.95 ETH.
Capture tx 0x0e7680…8705, block 25980525, index 0. 175 calls, depth 24, 39 logs. Every call succeeded.

04 · How the capture tx ran

From yoink() to the frozen pile

The LP module is a proxy. Its beacon points at a logic contract. On chain, the named functions are safe and initialize. We do not name a vendor. After the self-batch, ordinary calls do the rest: approve the aToken, Permit2, approve one wei of PAT, mint the position, clear the approvals.

After the mint, the helper talks to the unlocker. Transfers show the helper getting aEthrsETH and paying PAT. Pool Manager take() sends the aToken over. The helper then calls Aave withdraw with amount max, user and receiver both itself. aEthrsETH burns. rsETH lands on the helper and moves to the Yoink bot, which sells a slice on a real ETH/rsETH v4 pool and parks the rest.

Call rangeWhat it did
0–24yoink() into the helper, Aave probe, zero-amount aToken transfer
25–31batch(self) → gateway module → Safe exec, operation 1, delegate call execute
32–110LP module: Permit2, mint UNI-V4-POSM, 2,900 aEthrsETH into Pool Manager
111–133Unlocker swap + take aEthrsETH to helper; helper pays 2,905.81 PAT
134–156Aave withdraw, burn aEthrsETH, rsETH to helper, then to Yoink bot
157–174Sell 17.63 rsETH for 18.95 ETH; park 2,882.37 at 0xC70f00…; skim ETH
Where the 2,900 rsETH went in the capture txTransfers inside 0x0e7680…8705
Victim Safe0x40E93a…7AbA8v4 PAT poolhooks = 0Aave unwrapuser = helperYoink bot@yoink69802,882.37 rsETHstill at 0xC70f00…17.63 rsETHsold for 18.95 ETH
Value flow, not control flow. Yoink is the outer caller. The Safe never signed this tx.

05 · Where the rsETH sits now

A freeze wallet, a skim, and a later seize

The bulk never left the first landing spot. The Yoink bot sent it to a reused wallet that has been live since late 2022. No rsETH has left that wallet since the capture. Native ETH on that same wallet is old money. The capture-day ETH skim went to a second Yoink wallet. Watching whether that pile ever moves is what MoneyFlow is for.

Kelp's public notes speak of a wallet pause. We did not see an admin pause call. The chain shows the zero outflow.

An hour later Yoink came back for leftover aToken. That second hit is an Aave seize. Yoink took a WETH flash loan from Morpho Blue, then called liquidationCall with collateral rsETH, debt WETH, user the Safe. Aave's event records the WETH covered and the rsETH seized. The net debt-token transfer is smaller because Aave mints accrued interest first. Yoink sold the seized rsETH on Fluid, repaid Morpho, and kept a thin ETH skim.

The first drain pulled aToken and left the WETH debt in place. Health fell. The seize is what that drop made possible.

Fate of rsETH taken from this Safe15 September 2026, Bitquery transfers
rsETH02,900Still sitting2,882.37Aave seize157.71Copycats77Sold in-block17.63
Copycat drips are aEthrsETH that went into PAT pools and were unwrapped the same way as the primary. Dollar print uses $2,706.4 per rsETH from the capture quote on the frozen pile.
When UTCWhat moved
04:38:472,882.37 rsETH to 0xC70f00… ; 17.63 sold for 18.95 ETH, skim to 0x6c42…
05:24:47Copycat 50 aEthrsETH, same PAT recipe. 40 rsETH into CoW in four tens, 10 to 0x006d0e0d…
05:43–05:58Repeat v4 PAT deposits (two nines, then 0.5 drips). Same Safe, same module path.
05:47:59Same attacker hits a second Safe: 86,632 DUSD and 38,248 USDC out; 23.69 ETH to the attacker EOA.
05:53:59Yoink Aave seize: 168.83 WETH debt covered, 157.71 rsETH taken, sold on Fluid.
06:00Owner tx disables the two modules used in this drain (gateway and LP).
06:37Same sender repays 248 WETH of Aave debt.
07:20Owner tx disables nine other modules on the same Safe.

06 · What is still on the Safe

The loop did not empty

After the extracts, the seize and the WETH repay, a merge snapshot of the Safe still shows a large aEthrsETH pile and a large WETH debt-token balance. Native rsETH left on the Safe is a few tokens. UNI-V4-POSM still includes the empty PAT NFT. Other token balances on this address look like spam.

Still on the Safe after the morningAmount
aEthrsETH50,279
variableDebt WETH51,344
Native rsETH7.18
UNI-V4-POSM NFTs18, including the empty PAT position

The two modules used in the drain were turned off that morning, about an hour and a half after the capture. Copycats ran until a few minutes before that. The leftover Aave position is still open.

A second Safe was hit later that morning by the same attacker, with a fresh PAT mint. Yoink did not front-run that one. DUSD and USDC went into Pool Manager, the helper swapped them out, and ETH landed on the attacker. Public notes put the dollar size near the DUSD plus USDC.

If you run a Safe with modules
A module that can call execTransactionFromModule can move funds without a fresh owner signature. A public batch contract in front of that module is the path we measured here.
Off
Turn off modules you do not use
List
Treat a public batch target as hostile until proven
Debt
Pulling aToken without paying debt invites a seize

07 · Did labels help?

Our database named venues. MetaSleuth named Yoink.

We queried labels and wrote nothing back.

Our directory.labels table has no rows for the victim Safe, the executor, the attacker, PAT, the modules, Yoink, or the freeze wallet. It names well-known venues through the address labels API: Uniswap v4 Pool Manager, CoW settlement, Morpho Blue, Fluid. That confirms routes we already had from the trace. It does not name anyone in the case.

MetaSleuth names the searcher MEV Frontrunner Yoink. It also names Safe Singleton 1.3.0, Aave V3, aEthrsETH, Kelp rsETH, Permit2, and Uniswap v4 Position Manager. The Yoink EOA also carries an EXPLOIT tag from an older Aperture Finance case.

Early public notes had Morpho and Fluid swapped. The flash loan is Morpho Blue. The rsETH-to-ETH swap is Fluid.

08 · Related, and not the same

April Kelp, and a second Safe

In April a bridge mint created a large pile of unbacked rsETH. Different people, and a different date. This September event is a user module path. Kelp's rsETH backing is not the failure mode here.

The second Safe is the same PAT recipe against other tokens, same attacker, no Yoink front-run. We treat it as the same crew, a smaller take.

09 · How we read the chain

Transfers first, then events, then labels

Where a swap event and a transfer disagree, we kept the transfer. The same queries sit behind Bitquery MCP.

What could go wrongHow we handled it
Swap event signs vs transfersTransfers are ground truth: Safe → Pool Manager → helper for aEthrsETH, helper pays PAT. take() matches.
Debt token 8.71 vs 168.83 WETHAave Burn splits interest mint and repay. LiquidationCall event matches WETH covered and rsETH seized.
Hook vs unlockerPoolKey.hooks is the zero address at initialize and at mint. 0x5d20 is a CALL into unlock.
Merge balances on the SafeaEthrsETH and variableDebtWETH are the two we trust; other tokens on the address are treated as spam.
Kelp pause as a factWe report zero rsETH outflows. We do not claim we saw the pause call.

10 · The record

The addresses behind the story

RoleAddress
Victim Safe 1.3.00x40E93a…7AbA8
Capture tx0x0e7680…8705
Public executor0x4f00…8ebC
Gateway module0xea18…c4ca
Uniswap v4 LP module0xdcdc…82ab
Original attacker EOA0x0dC2…9b66
Helper0x1060…5Cb1
PAT0x8762…61f5
Unlocker0x5d20…e8bb
Yoink EOA0xFDe0…455A
Yoink bot0x80BF…D4e4
rsETH still here0xC70f00…80ea0
ETH skim0x6c42…bf0e
Second Safe0x6a1f…7ea2
Owner-tx sender0x8c2a…fee8

The same hops are on Coinpath.

If you hold rsETH in a Safe that uses a public executor in front of an LP module, the two modules in this case are worth treating as burned until you have turned them off yourself. An empty capture-day balance is no proof the path is closed.

Run it yourself

Ask these questions in plain English

The same index is on the Bitquery MCP server. An assistant can list which modules have been calling a Safe, decode a capture tx, follow an aToken to rsETH, or watch a wallet for outflows, without writing the query by hand.

List module execs into any Safe, with operation 0 vs 1Follow aEthrsETH from a wallet into Uniswap v4 and back outRead Aave seize events: debt covered vs collateral takenWatch a receiver for rsETH outflows after a drain
Explore Bitquery MCP Figures measured 15 September 2026 against Bitquery's Ethereum index (eth_api calls, events and transfers).
Legal disclaimer

This article is provided for informational and educational purposes only and reflects analysis of publicly available on-chain data as of the dates indicated. It does not constitute legal, financial, compliance, or investment advice, and nothing in it is a recommendation to buy, sell, or hold any token or asset.

This piece reconstructs one Safe on Ethereum on 15 September 2026 from public call, event and transfer records. Yoink is a public searcher name from MetaSleuth; we describe that bot by its on-chain behaviour and do not claim who runs it. Module, executor and attacker addresses are named by behaviour. We do not claim a vendor for the unverified LP logic, and we do not claim Kelp’s contracts failed. Dollar figures use the capture-day print on the frozen rsETH pile. Owner identity is unlabeled. Aave seize math uses the LiquidationCall event, not the net debt-token transfer. Blockchain addresses are pseudonymous, and a transaction between two addresses does not by itself establish the identity, intent or knowledge of any party. Every entity attribution here is an inference that may be incomplete or wrong.

Nothing herein should be relied upon as a definitive determination of fact. Readers should conduct their own independent verification before taking any action. The authors and publisher accept no liability for any loss or damage arising from reliance on this material. All trademarks and company names are the property of their respective owners.

Run this kind of trace on your own data

Every figure here came from public Ethereum call, event and transfer records: Safe module execs, Uniswap v4 unlocks, Aave burns and the wallets the rsETH landed in.