Case studyWhite-hat recoveries2021–2022
WHG_1 gets there first.
This address (0x69670b0c1b100739812415dd474804bb32b3aeca) is one that I use to send all compromised funds I come across in the space. It has been involved in returning ~$1M to users.
By not using the public txpool, I was able to rescue assets before a bad actor was able to after compromising the private key
- #3Gas in0.0090 ETH from the funding addressView transaction 0xc588…5254 ↗
- #4Sweep1,141,159.2 HEX → WHG_1View transaction 0x8962…3d68 ↗
Two transactions, one block: the gas arrives and is spent before anyone else can touch it.
- returned to users
- ~$1M
- public cases
- 17
- largest single case (Case 09)
- $490k
- sweeps in the same block as their gas
- 17/17
- transaction types
- 6
How a rescue lands
The attacker has the same private key. Any ETH sent to the wallet for gas can be swept before it's spent, so the trick is never leaving it there.
April–July 2021 · Cases 02–05
Before London
The compromised wallet's transactions paid a 0 gwei gas price, so the wallet never needed ETH at all. In the same block, the funding address paid the block producer instead, through Flashbots' FlashbotsCheckAndSend contract.
- #0Sweep · 0 gwei5,990.14 SWAP → WHG_1View transaction 0x8264…fcf8 ↗
- #1Miner payment0.025 ETH to the block producer via FlashbotsCheckAndSendView transaction 0xa9b8…ce7d ↗
August 2021 onwards · Cases 06–17
After London
EIP-1559 (the London fork, block 12,965,000) made 0-gwei transactions invalid. So each step became a pair: the funding address sends ETH for gas, and the compromised wallet spends it in the very next transaction of the same block.
- #0Gas in0.0097 ETH from the funding addressView transaction 0xde63…89fd ↗
- #1Sweep16,660 XTM → WHG_1View transaction 0x093e…ee8d ↗
Six ways value gets stuck
Every case is one of these shapes. Pick one to see what it is and how it was moved.
Staking withdrawals
- What it is
- Tokens sitting in a staking contract can't be moved until the contract releases them. The attacker holds the same private key, so the moment they're released they're fair game for whoever moves first.
- On-chain
- Two calls from the compromised wallet. First the unlock: executeWithdrawal on TrustSwap staking, withdrawDelegated on The Graph staking, stakeEnd on HEX, or unstake on Staked TrueFi. Then an ERC-20 transfer of the released tokens to WHG_1.
- Why it was risky
- Both calls need ETH for gas in a wallet the attacker is watching, and an unlock broadcast to the public mempool announces that tokens are about to become movable.
- How it was handled
- Each call landed in the same block as the ETH that paid for it. Case 09 is the biggest: stakeEnd on HEX in block 13,386,363, then 1,141,159.2 HEX to WHG_1 three blocks later.
The case files
Each case as I wrote it, with the transactions decoded underneath. Every hash links to Etherscan.
Case 01
The hand-back$76kI rescued a total of $76k from the compromised address. The assets were bridged over from Optimistic to Ethereum Mainnet and swept to WHG_1.
- 4,763.53 SNX→ 0xef33…0003
1 on-chain step · the hand-back out of WHG_1
- Hand-backBlock 12,318,583 · #2404,763.53 SNX → 0xef33…0003View transaction 0x4a76…f01b ↗
Case 02
Staking withdrawals$24kI rescued a total of $24k from the compromised address. The assets were unstaked from the TrustSwap staking contract and swept to WHG_1.
- 5,990.14 SWAP→ WHG_1
3 on-chain steps · every sweep in the same block as its gas
- UnlockBlock 12,336,882 · #0executeWithdrawal on TrustSwap stakingView transaction 0x879c…b179 ↗
- SweepBlock 12,336,920 · #05,990.14 SWAP → WHG_1View transaction 0x8264…fcf8 ↗
- Miner paymentBlock 12,336,920 · #10.025 ETH to the block producer via FlashbotsCheckAndSendView transaction 0xa9b8…ce7d ↗
Compromised wallet 0xe38f…904f
Case 03
Staking withdrawals$19.2kI rescued a total of $19.2k from the compromised address. The assets were unstaked from TheGraph staking contract and swept to WHG_1.
- 15,013.37 GRT→ WHG_1
3 on-chain steps · every sweep in the same block as its gas
- UnlockBlock 12,419,886withdrawDelegated on The Graph stakingView transaction 0xd48b…6e3a ↗
- SweepBlock 12,419,991 · #015,013.37 GRT → WHG_1View transaction 0x818b…9221 ↗
- Miner paymentBlock 12,419,991 · #10.031 ETH to the block producer via FlashbotsCheckAndSendView transaction 0xaa60…296a ↗
Compromised wallet 0x3b54…e767
Case 04
Staking withdrawals$2.9kI rescued a total of $2.9k from the compromised address. The assets were unstaked from the TrustSwap staking contract and swept to WHG_1.
- 4,014.86 SWAP→ WHG_1
3 on-chain steps · every sweep in the same block as its gas
- UnlockBlock 12,781,834 · #0executeWithdrawal on TrustSwap stakingView transaction 0x6207…656f ↗
- SweepBlock 12,781,837 · #04,014.86 SWAP → WHG_1View transaction 0x8469…5589 ↗
- Miner paymentBlock 12,781,837 · #10.01 ETH to the block producer via FlashbotsCheckAndSendView transaction 0xd4c1…0f33 ↗
Compromised wallet 0x82b8…c419
Case 05
Staking withdrawals$9.5kI rescued a total of $9.5k from the compromised address. The assets were unstaked from TheGraph staking contract and swept to WHG_1.
- 6,016.21 GRT→ WHG_1
- 10,193.79 GRT→ WHG_1
6 on-chain steps · every sweep in the same block as its gas
- UnlockBlock 12,829,303 · #5withdrawDelegated on The Graph stakingView transaction 0xbd49…c3e9 ↗
- SweepBlock 12,829,313 · #06,016.21 GRT → WHG_1View transaction 0xe200…2adc ↗
- Miner paymentBlock 12,829,313 · #10.005 ETH to the block producer via FlashbotsCheckAndSendView transaction 0x8ebe…1b9a ↗
- UnlockBlock 12,829,319 · #1withdrawDelegated on The Graph stakingView transaction 0x8eda…5472 ↗
- SweepBlock 12,829,321 · #110,193.79 GRT → WHG_1View transaction 0x66a9…467d ↗
- Miner paymentBlock 12,829,321 · #20.005 ETH to the block producer via FlashbotsCheckAndSendView transaction 0xd4e0…e2a6 ↗
Compromised wallet 0xae97…42e4
Case 06
Staking withdrawals$20.6kI rescued a total of $20.6k from the compromised address. The assets were unstaked from TheGraph staking contract and swept to WHG_1.
- 27,696.34 GRT→ WHG_1
4 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 12,995,351 · #30.0068 ETH from the funding addressView transaction 0xd424…5935 ↗
- UnlockBlock 12,995,351 · #4withdrawDelegated on The Graph stakingView transaction 0x66fe…09bd ↗
- Gas inBlock 12,995,354 · #20.0023 ETH from the funding addressView transaction 0x525f…e9de ↗
- SweepBlock 12,995,354 · #327,696.34 GRT → WHG_1View transaction 0xff79…2d43 ↗
Compromised wallet 0xc80e…79fb
Case 07
Staking withdrawals$10.4kI rescued a total of $10.4k from the compromised address. The assets were unstaked from the TrustSwap staking contract and swept to WHG_1.
- 9,399.88 SWAP→ WHG_1
4 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,262,889 · #20.0261 ETH from the funding addressView transaction 0x6941…594a ↗
- UnlockBlock 13,262,889 · #3executeWithdrawal on TrustSwap stakingView transaction 0x0af1…b8b9 ↗
- Gas inBlock 13,262,898 · #10.0117 ETH from the funding addressView transaction 0x1083…064c ↗
- SweepBlock 13,262,898 · #29,399.88 SWAP → WHG_1View transaction 0x5d48…5f20 ↗
Compromised wallet 0xe097…e1e0
Case 08
Staking withdrawals$7.7kI rescued a total of $7.7k from the compromised address. The assets were unstaked from TheGraph staking contract and swept to WHG_1.
- 10,884.62 GRT→ WHG_1
4 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,334,240 · #20.0202 ETH from the funding addressView transaction 0x376a…e6b4 ↗
- UnlockBlock 13,334,240 · #3withdrawDelegated on The Graph stakingView transaction 0xca62…b71d ↗
- Gas inBlock 13,334,248 · #00.0065 ETH from the funding addressView transaction 0xc98d…eaf9 ↗
- SweepBlock 13,334,248 · #110,884.62 GRT → WHG_1View transaction 0xc397…715b ↗
Compromised wallet 0x8a09…c3a9
Case 09
Staking withdrawals$490kI rescued a total of $490k from the compromised address. The assets were unstaked from the HEX staking contract and swept to WHG_1.
- 1,141,159.2 HEX→ WHG_1
4 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,386,363 · #00.0255 ETH from the funding addressView transaction 0xd288…56e2 ↗
- UnlockBlock 13,386,363 · #1stakeEnd on the HEX contractView transaction 0x16bb…35ed ↗
- Gas inBlock 13,386,366 · #30.0090 ETH from the funding addressView transaction 0xc588…5254 ↗
- SweepBlock 13,386,366 · #41,141,159.2 HEX → WHG_1View transaction 0x8962…3d68 ↗
Compromised wallet 0xe097…e1e0
Case 10
Staking withdrawals$8.8kI rescued a total of $8.8k from the compromised address. The assets were unstaked from the TruFi staking contract and swept to WHG_1.
- 20,402.77 TRU→ WHG_1
4 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,406,749 · #110.0599 ETH from the funding addressView transaction 0x41ee…ed3f ↗
- UnlockBlock 13,406,749 · #12unstake on Staked TrueFi (stkTRU)View transaction 0xd67f…e67d ↗
- Gas inBlock 13,406,755 · #00.0112 ETH from the funding addressView transaction 0x4586…6702 ↗
- SweepBlock 13,406,755 · #120,402.77 TRU → WHG_1View transaction 0x67a5…38f0 ↗
Compromised wallet 0x470b…c5b9
Case 11
The hand-back$3.5kI rescued a total of $3.5k from the compromised address. The assets representing the staked amount in the TruFi staking contract were swept to WHG_1.
- 2,106 stkTRU→ 0x4a88…1e3f
1 on-chain step · the hand-back out of WHG_1
- Hand-backBlock 13,646,583 · #1102,106 stkTRU → 0x4a88…1e3fView transaction 0xcdd0…ce40 ↗
Case 12
The hand-back$1.2kI rescued a total of $1.2k from the compromised address.
- 2.1137 PBR→ 0x75b8…1c30
- 0.2349 PBR→ Zero address
- 467.35 PBR→ 0x4a88…1e3f
1 on-chain step · the hand-back out of WHG_1
- Hand-backBlock 13,646,439 · #146467.35 PBR → 0x4a88…1e3fView transaction 0xbe16…846c ↗
Case 13
Liquid ERC-20$39.9kI rescued a total of $39.9k from the compromised address.
- 16,660 XTM→ WHG_1
2 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,646,819 · #00.0097 ETH from the funding addressView transaction 0xde63…89fd ↗
- SweepBlock 13,646,819 · #116,660 XTM → WHG_1View transaction 0x093e…ee8d ↗
Compromised wallet 0x222c…eedb
Case 14
Receipt tokens$46.9kI rescued a total of $46.9k from the compromised address. It required redeeming tokens on the MantraDAO ZENTREST contract to unlock the value.
- 216,695.03 OM→ WHG_1
4 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,646,992 · #50.0488 ETH from the funding addressView transaction 0x3a7a…1b4e ↗
- UnlockBlock 13,646,992 · #6redeem on ZENTEREST OMView transaction 0xe3e1…9dab ↗
- Gas inBlock 13,646,995 · #00.0135 ETH from the funding addressView transaction 0xe3af…5655 ↗
- SweepBlock 13,646,995 · #1216,695.03 OM → WHG_1View transaction 0x4996…b05c ↗
Compromised wallet 0x222c…eedb
Case 15
Staked liquidity$15kI rescued a total of $15k from the compromised address. It required transferring a liquidity position on SushiSwap to WHG_1.
- 3.2202 SLP→ WHG_1
- 3.2886 SLP→ WHG_1
10 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,680,551 · #00.0430 ETH from the funding addressView transaction 0x7888…9658 ↗
- UnlockBlock 13,680,551 · #1unstake on the Illuvium core poolView transaction 0x9bfd…1144 ↗
- Gas inBlock 13,680,563 · #10.0147 ETH from the funding addressView transaction 0xa2a5…a69f ↗
- SweepBlock 13,680,563 · #23.2202 SLP → WHG_1View transaction 0x4890…bb8f ↗
- Gas inBlock 13,680,677 · #00.0535 ETH from the funding addressView transaction 0x5db5…d621 ↗
- UnlockBlock 13,680,677 · #1processRewards on the Illuvium core poolView transaction 0x5d03…77db ↗
- Gas inBlock 13,680,685 · #30.0366 ETH from the funding addressView transaction 0xfd3e…ccc6 ↗
- UnlockBlock 13,680,685 · #4unstake on the Illuvium core poolView transaction 0xe30a…2f81 ↗
- Gas inBlock 13,680,688 · #00.0090 ETH from the funding addressView transaction 0xaaa2…ac9b ↗
- SweepBlock 13,680,688 · #13.2886 SLP → WHG_1View transaction 0x4425…e885 ↗
Compromised wallet 0x6214…9798
Case 16
NFTs and grantsI rescued an UnstoppableDomains NFT from the compromised address.
- .crypto #474785…7861→ 0xe8e0…9581
2 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,699,442 · #20.0120 ETH from the funding addressView transaction 0x3b24…35d4 ↗
- SweepBlock 13,699,442 · #3.crypto #474785…7861 → 0xe8e0…9581View transaction 0xcbd5…5097 ↗
Compromised wallet 0x8a09…c3a9
Case 17
NFTs and grants$31k + $160kI rescued 1x Doodles NFT (floor ~12E / $31k) and an NFT representing an investment token lockup worth ~$160k.
- Goldfinch V2 Community Rewards Tokens #206→ WHG_1
- Doodles #9038→ WHG_1
6 on-chain steps · every sweep in the same block as its gas
- Gas inBlock 13,998,872 · #00.0841 ETH from the funding addressView transaction 0x871a…4566 ↗
- UnlockBlock 13,998,872 · #1acceptGrant on the Goldfinch MerkleDistributorView transaction 0x515e…4dfc ↗
- Gas inBlock 13,998,874 · #00.1609 ETH from the funding addressView transaction 0x0311…230e ↗
- SweepBlock 13,998,874 · #1Goldfinch V2 Community Rewards Tokens #206 → WHG_1View transaction 0x662c…78d5 ↗
- Gas inBlock 13,999,519 · #210.0146 ETH from the funding addressView transaction 0x91bf…a5ef ↗
- SweepBlock 13,999,519 · #22Doodles #9038 → WHG_1View transaction 0x4816…0dab ↗
Compromised wallet 0x2fa4…85d1
Steps, blocks and amounts are decoded from the public transactions: receipts and Transfer logs, with call names from verified contracts. The funding address 0x44a3…77f2 appears in every rescue from Case 02 to Case 17.
