# PendleRouterSwapHook reverts because of wrong decoding in _decodeTokenOut for swapExactPt- ForToken

PendleRouterSwapHook mis-decodes the dynamic TokenOutput struct in _decodeTokenOut, reading an ABI offset as a token address and reverting every swapExactPtForToken.

- Payout: Contest reward
- Severity: high
- Program: Superform
- Credited: Christoph Michel
- Disclosed: 2025-07-17
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_superform_may2025.pdf#3.1.4
- Library page: https://bugbountylibrary.com/r/cantina%3Asuperform_may2025%3A3.1.4
- Tags: domain:web3 impact:dos kind:contest platform:cantina sev:high tech:evm tech:solidity technique:source-review vuln:dos vuln:misconfiguration

**Target:** PendleRouterSwapHook.sol preExecute/_decodeTokenOut (Solidity hook)
**Root cause:** TokenOutput contains a dynamic SwapData field, so it is encoded as an offset pointer rather than inline; _decodeTokenOut(data[57:]) reads that offset (e.g. 0xa0) as the tokenOut address instead of following it.
**Steps:**
1. Fork mainnet at block 22581741-1.
2. Build hookData = abi.encodePacked(bytes4(0), yieldSource, false, uint256(0), txData) where txData is the swapExactPtForToken calldata from tx 0x202cfd7e...
3. Call hook.preExecute(prevHook, user, hookData).
4. Observe _getBalance -> _decodeTokenOut(data[57:]) returns 0x...a0 and calls 0x...a0::balanceOf(), which reverts on uint256 decode.
**How found:** Code-reading of the Pendle TokenOutput/SwapData struct layout: noticed SwapData has a dynamic bytes extCalldata, making TokenOutput dynamic, so the inline slice decoded at data[57:] is an offset, not an address.
**Insight:** When assembly-decoding a struct slice, check whether any nested field is dynamic (bytes/string/array) — that turns the whole struct into an offset pointer, so fixed-offset slicing reads the pointer instead of the value.
