Size GenericRoute Approval Drain
Exploit Transactions
0x17846acccd832432dba32bf1008797377324a1bd8bd8ef8e52ec8171afc99a810xc7477d6a5c63b04d37a39038a28b4cbaa06beb167e390d55ad4a421dbe4067f80x23ccb2f1dc6700c2f077bb400fb84dbf4d786390fe8fba6a5e4e1c1864221aceVictim Addresses
0xf4a21ac7e51d17a0e1c8b59f7a98bb7a97806f14Ethereum0x83eccb05386b2d10d05e1baea8ac89b5b7ea8290EthereumLoss Breakdown
Similar Incidents
Unibot Approval Drain
40%USDTStaking Approval Drain
39%dYdX Callback Approval Drain
38%Flooring extMulticall Approval Drain
37%Dexible selfSwap allowance drain
33%TheNFTV2 Stale Burn Approval
33%Root Cause Analysis
Size GenericRoute Approval Drain
1. Incident Overview TL;DR
An unprivileged adversary used Size's public LeverageUp zap at 0xF4a21Ac7e51d17A0e1C8B59f7a98bb7A97806f14 to drain 20000 PT-wstUSR-25SEP2025 from tradcorp.eth (0x83eCCb05386B2d10D05e1BaEa8aC89b5B7EA8290). The attacker deployed a helper contract, initialized it, invoked LeverageUp through the helper, and then withdrew the stolen PT balance. The loss did not require protocol ownership, compromised keys, or privileged roles.
The root cause is that LeverageUp exposes DexSwap._swapGenericRoute to public callers and that branch performs an unrestricted external call with attacker-chosen target and calldata. That breaks the invariant that zap routing should only perform authentic swaps over zap-controlled funds. In the exploit trace, LeverageUp first observes the victim's standing PT allowance and then executes PendlePrincipalToken::transferFrom(victim, helper, 20000000000000000000000), turning the victim's prior approval into an unauthorized transfer primitive.
2. Key Background
Size's LeverageUp flow accepts a caller-supplied market address plus an array of swap route descriptions. One supported route type is GenericRoute, intended to let the zap call an arbitrary router while preparing leverage operations. PT-wstUSR-25SEP2025 at 0x23E60d1488525bf4685f53b3aa8E676c30321066 is a Pendle principal token with standard ERC20 allowance semantics.
That combination is dangerous when the zap itself is the approved spender. If a victim has already approved the zap, and the zap lets public callers convert route data into arbitrary low-level calls, the zap can be tricked into spending that approval outside any legitimate market action. The collected helper analysis shows the attacker used a purpose-built fake market contract that returns plausible values for riskConfig, data, oracle, deposit, and ERC20-like helper methods so that LeverageUp's interface checks succeed.
3. Vulnerability Analysis & Root Cause Summary
This is an ATTACK-category ACT incident caused by an arbitrary-call surface inside a public approval-bearing zap. The critical issue is not a broken Pendle token invariant; it is Size's decision to let user-controlled GenericRoute data determine both the callee and calldata of a raw external call. In DexSwap._swapGenericRoute, the zap grants an approval to the supplied router and then executes (bool success,) = params.router.call(params.data); without authenticating the router, selector, recipient, token source, or linkage to a valid Size market. LeverageUp.leverageUpWithSwap exposes this path directly to public callers through SwapParams[].
The exploit combines two ingredients. First, the victim had already granted LeverageUp a standing PT allowance of exactly 20000000000000000000000. Second, the attacker supplied a fake market contract that satisfied LeverageUp's read-only checks while packaging GenericRoute(router = ptToken, tokenIn = helper, data = abi.encodeWithSelector(transferFrom, victim, helper, 20000e18)). When LeverageUp processed that route, it invoked the PT token directly and spent the victim's allowance in favor of the attacker-controlled helper.
4. Detailed Root Cause Analysis
The relevant protocol invariant is straightforward: a leverage zap may route swaps over assets it legitimately controls for the current user flow, but it must never expose a generic path that can trigger unrelated external effects using third-party approvals. The code-level breakpoint is the GenericRoute execution site in Size's zap logic:
(bool success,) = params.router.call(params.data);
The incident's pre-state is Ethereum mainnet immediately before block 23145764, after the victim already held PT-wstUSR-25SEP2025 and had already approved LeverageUp for 20000e18 PT. The attacker then used a four-transaction sequence:
0x17846acccd832432dba32bf1008797377324a1bd8bd8ef8e52ec8171afc99a81deployed helper0xa6dc1fc33c03513a762cdf2810f163b9b0fd3a71.0xda91d19080f799f609eb5c513439945afce65601caa066e32098efd32fbeb1b9initialized the helper.0xc7477d6a5c63b04d37a39038a28b4cbaa06beb167e390d55ad4a421dbe4067f8executed the exploit through LeverageUp.0x23ccb2f1dc6700c2f077bb400fb84dbf4d786390fe8fba6a5e4e1c1864221acewithdrew the stolen PT from the helper.
The trace for the exploit transaction shows the semantic breakpoint directly:
PendlePrincipalToken::allowance(
0x83eCCb05386B2d10D05e1BaEa8aC89b5B7EA8290,
0xF4a21Ac7e51d17A0e1C8B59f7a98bb7A97806f14
) = 20000000000000000000000
PendlePrincipalToken::transferFrom(
0x83eCCb05386B2d10D05e1BaEa8aC89b5B7EA8290,
0xA6dc1FC33C03513A762cdf2810f163B9B0FD3a71,
20000000000000000000000
)
The helper analysis explains why the fake market was sufficient. Its data() function embeds the PT token and current contract address, riskConfig() returns permissive values, deposit() is a no-op, and ERC20-like methods such as allowance, balanceOf, approve, and transferFrom return values that allow LeverageUp to continue execution. That means LeverageUp never authenticates the market argument and never binds GenericRoute to a legitimate swap.
5. Adversary Flow Analysis
The adversary cluster consists of EOA 0xa7e9b982b0e19a399bc737ca5346ef0ef12046da and helper contract 0xa6dc1fc33c03513a762cdf2810f163b9b0fd3a71. The deployment tx created the helper; the initialization tx enabled its owner-gated entrypoint; the exploit tx made the helper call LeverageUp; and the final tx swept the stolen PT back to the EOA.
At a high level, the helper wrapped a call into LeverageUp.leverageUpWithSwap and supplied attacker-controlled SwapParams. The helper advertised itself as a market-like contract by exposing the functions LeverageUp expected to call. Inside the exploit path, the attacker chose GenericRoute and encoded a PT transferFrom instead of a legitimate swap. Because the victim had already approved LeverageUp, the zap itself became the authorized spender and executed the transfer on the victim's behalf.
The helper then held the stolen PT until the attacker called its withdrawal routine. The collected helper behavior summary shows an owner-only withdrawERC20(address[]) routine that transfers each token balance to the stored owner. That closes the loop from arbitrary external call, to victim token loss, to attacker-controlled asset realization.
6. Impact & Losses
The measurable loss is the direct theft of 20000 PT-wstUSR-25SEP2025 from tradcorp.eth. In smallest units, the loss is 20000000000000000000000 with 18 decimals. The transfer path is not an accounting artifact or valuation estimate; it is a concrete unauthorized ERC20 transfer backed by the victim's existing approval to the Size zap.
The exploit conditions are also clear. A victim must have granted LeverageUp token allowance, the attacker must be able to submit GenericRoute data to the public zap, and the attacker must choose calldata that converts the zap's spender privilege into a third-party transfer. All three conditions are satisfied in the recorded exploit.
7. References
- Exploit trace:
0xc7477d6a5c63b04d37a39038a28b4cbaa06beb167e390d55ad4a421dbe4067f8 - Helper deployment tx:
0x17846acccd832432dba32bf1008797377324a1bd8bd8ef8e52ec8171afc99a81 - Helper withdrawal tx:
0x23ccb2f1dc6700c2f077bb400fb84dbf4d786390fe8fba6a5e4e1c1864221ace - Size LeverageUp / DexSwap contract:
0xF4a21Ac7e51d17A0e1C8B59f7a98bb7A97806f14 - Victim address:
0x83eCCb05386B2d10D05e1BaEa8aC89b5B7EA8290 - Token address:
0x23E60d1488525bf4685f53b3aa8E676c30321066 - Helper contract analysis and selector map for
0xa6dc1fc33c03513a762cdf2810f163b9b0fd3a71