Uwerx Pool Drain
Exploit Transactions
0x3b19e152943f31fe0830b67315ddc89be9a066dc89174256e17bc8c2d35b5af8Victim Addresses
0x4306b12f8e824ce1fa9604bbd88f2ad4f0fe3c54EthereumLoss Breakdown
Similar Incidents
NOON Pool Drain via Public transfer
41%Minimal-Proxy Pool Reinitializer Drain
35%VINU Reserve Drain
35%Orion Pool Double-Count Exploit
33%pETH Pair Drain via Synthetic balanceOf Inflation
33%DAppSocialPoolModel Alt-Withdraw Drain
33%Root Cause Analysis
Uwerx Pool Drain
1. Incident Overview TL;DR
On Ethereum mainnet block 17826203, transaction 0x3b19e152943f31fe0830b67315ddc89be9a066dc89174256e17bc8c2d35b5af8 drained the public WERX/WETH Uniswap V2 pool at 0xa41529982bcccdfa1105c6f08024df787ca758c4. The attacker used a helper contract plus a zero-fee Balancer flash loan, bought WERX, collapsed the pair's WERX reserve to 100, then sold retained WERX back into the broken pool and realized 174780439904115214249 wei of net ETH profit.
The root cause was in the verified Uwerx token at 0x4306b12f8e824ce1fa9604bbd88f2ad4f0fe3c54. The token initialized both marketingWalletAddress and uniswapPoolAddress to address(1), and its special transfer branch credited the full amount to the recipient before burning 1% from the sender. When the public pair executed skim(address(1)), the pair itself became the token sender, so the buggy transfer path burned the pair's balance and enabled a permissionless reserve-collapse sequence.
2. Key Background
Two public Uniswap V2 maintenance functions are central here:
skim(to)transfers token balances above stored reserves toto.sync()updates stored reserves to match current token balances.
The Uwerx token applies custom fee logic when to == uniswapPoolAddress. Because uniswapPoolAddress remained address(1) from deployment, any transfer to address(1) was treated as a pool-directed transfer even though address(1) is not the pool.
The attack surface was therefore fully public: a user only needed access to the live pool, the public Balancer vault, and ordinary transaction submission.
3. Vulnerability Analysis & Root Cause Summary
The vulnerable logic is in the verified Uwerx ERC20 implementation. The contract initializes marketingWalletAddress and uniswapPoolAddress to address(1). Its _transfer function first subtracts amount from from and adds the full amount to to. If to == uniswapPoolAddress, it then emits reduced transfer events, emits a marketing transfer, and burns burnAmount from from. That means the actual balance updates no longer match the intended pool-fee accounting.
The relevant code path is:
address private marketingWalletAddress = 0x0000000000000000000000000000000000000001;
address private uniswapPoolAddress = 0x0000000000000000000000000000000000000001;
unchecked {
_balances[from] = fromBalance - amount;
_balances[to] += amount;
}
if (to == uniswapPoolAddress) {
uint256 userTransferAmount = (amount * 97) / 100;
uint256 marketingAmount = (amount * 2) / 100;
uint256 burnAmount = amount - userTransferAmount - marketingAmount;
emit Transfer(from, to, userTransferAmount);
emit Transfer(from, marketingWalletAddress, marketingAmount);
_burn(from, burnAmount);
}
The broken invariant is: when special pool-fee logic applies, recipient credit plus all side allocations must exactly match the sender debit, and only the real pool address must trigger that branch. Uwerx violates both requirements.
4. Detailed Root Cause Analysis
The exploit sequence follows directly from the code and trace:
- The attacker helper contract borrowed
20000000000000000000000WETH from the Balancer vault with zero fee. - It called
sync()on the WERX/WETH pair so reserves matched the live balances before manipulation. - It spent the flash-loaned WETH to buy WERX from the pair, receiving
5053637872806935777652180WERX according to the trace. - It transferred
4429817738575912760684500WERX back to the pair as deliberate excess inventory. - It then called
skim(address(1)). Becauseskimtransfers token excess from the pair toaddress(1), the pair becamefromin Uwerx_transferandaddress(1)becameto. - Since
to == uniswapPoolAddress == address(1), Uwerx entered the buggy branch and burned44298177385759127606845WERX from the pair after already creditingaddress(1)with the full skim amount. - The trace records the pair's WERX balance dropping to exactly
100; the nextsync()stored that manipulated reserve. - The helper then sold the retained
623820134231023016967680WERX into a pool that still held20174786100489116297837WETH, receiving20174786100489116297833WETH and leaving the pair with only4wei of WETH.
The decisive on-chain evidence appears in the execution trace:
0xa4152998...::skim(0x0000000000000000000000000000000000000001)
0x4306b12f...::transfer(0x0000000000000000000000000000000000000001, 4429817738575912760684500)
emit Transfer(src: pair, dst: 0x000...001, wad: 4296923206418635377863965)
emit Transfer(src: pair, dst: 0x991C13B8..., wad: 88596354771518255213690)
emit Transfer(src: pair, dst: 0x000...000, wad: 44298177385759127606845)
storage pair_balance: ... -> 100
The balance-diff artifact independently confirms the semantic end state:
{
"pair_werx_after": "623820134231023016967780",
"address1_werx_after": "4429817738575912760684500",
"attacker_profit_wei": "174780439904115214249"
}
5. Adversary Flow Analysis
The adversary cluster contains the EOA 0x6057a831d43c395198a10cf2d7d6d6a063b1fce4 and helper contract 0xda2ccfc4557ba55eada3cbebd0aeffcf97fc14ca. The EOA submitted the seed transaction and received the final ETH profit. The helper contract executed the flash loan, pool manipulation, and settlement.
The on-chain flow was:
- Flash-loan funding: Balancer vault
0xba12222222228d8ba445958a75a0704d566bf2c8lent20,000WETH with fee0. - Reserve distortion: the helper bought WERX, transferred excess WERX into the pair, executed
skim(address(1)), and calledsync()to lock in a100-WERX reserve. - Drain and settlement: the helper sold retained WERX into the manipulated pool, repaid the Balancer flash loan principal, withdrew the remaining WETH to ETH, and forwarded the ETH to the attacker EOA.
This entire lifecycle was realized in the single seed transaction 0x3b19e152943f31fe0830b67315ddc89be9a066dc89174256e17bc8c2d35b5af8.
6. Impact & Losses
The exploit drained the WERX/WETH pair's counter-asset reserve. After execution, the pair held only 4 wei of WETH and 623820134231023016967780 WERX. The attacker realized 174780439904115214249 wei of net ETH profit, as shown by the seed balance diff for the attacking EOA.
Affected component:
- Uwerx token
0x4306b12f8e824ce1fa9604bbd88f2ad4f0fe3c54 - WERX/WETH pair
0xa41529982bcccdfa1105c6f08024df787ca758c4
Loss summary:
ETH:"174780439904115214249"raw wei,18decimals
7. References
- Seed transaction:
0x3b19e152943f31fe0830b67315ddc89be9a066dc89174256e17bc8c2d35b5af8 - Attacker EOA:
0x6057a831d43c395198a10cf2d7d6d6a063b1fce4 - Helper contract:
0xda2ccfc4557ba55eada3cbebd0aeffcf97fc14ca - Uwerx token:
0x4306b12f8e824ce1fa9604bbd88f2ad4f0fe3c54 - WERX/WETH pair:
0xa41529982bcccdfa1105c6f08024df787ca758c4 - Balancer vault:
0xba12222222228d8ba445958a75a0704d566bf2c8 - Evidence used: transaction metadata, verbose execution trace, balance diff, and verified Uwerx source collected under the session artifacts