Reproduced Exploit

Paladin Valkyrie — recycled LP liquidity pays the reward token to the caller

transferFrom on the incentivized LP notifies the hook, which moves poolUserLiquidity from the holder to the recipient. Recycling that LP into the pool manager makes the manager the sole liquidity account. claim then pays the manager 100e18 of the reward token, a different token from the LP. A sweep…

Mar 2025•Other•logic•3 min read

Chain

Other

Category

logic

Date

Mar 2025

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 61562-rewards-can-be-stolen-when-incentivizederc20-tokens-are-recu. Standalone Foundry PoC and full write-up: 61562-rewards-can-be-stolen-when-incentivizederc20-tokens-are-recu_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/logic/reward-calculation · vuln/logic/missing-check

Key info#

ProtocolPaladin Valkyrie
FindingCyfrin, AuditVault 61562
Vulnerable codeIncentivizedERC20.transferFrom → notifyTransfer
HarmThe caller claims 100e18 of the reward token. The LP holder is paid 0.
Chainlocal synthetic

TL;DR#

transferFrom on the incentivized LP notifies the hook, which moves poolUserLiquidity from the holder to the recipient. Recycling that LP into the pool manager makes the manager the sole liquidity account. claim then pays the manager 100e18 of the reward token, a different token from the LP. A sweep takes that balance to the caller. The control reverts when the recycled LP notifies, and the holder claims the 100e18.

The vulnerable code#

SOLIDITY
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
    ITokenizedHook(hook).notifyTransfer(poolId, from, to, amount);
    return super.transferFrom(from, to, amount);
}

poolUserLiquidity[id][from] -= amount;
// @> VULN: recycled IncentivizedERC20 liquidity is credited to `to`
poolUserLiquidity[id][to] += amount;

The fix rejects notifyTransfer when the caller is the incentivized LP.

Root cause#

Liquidity accounting follows the ERC20 transfer with no check that the token is an incentivized LP being parked on a contract that will not keep the rewards. Whoever can call claim for the new holder receives the reward token.

Preconditions#

The holder has the whole LP liquidity. The incentive contract holds 100e18 of a separate reward token. No timestamp warp. One block.

Attack walkthrough#

  1. 100e18 of RWD is deposited. The pot is not the LP token.
  2. The holder is credited with 100e18 of LP liquidity and approves the pool manager.
  3. The manager transferFroms the LP. notifyTransfer credits the manager and zeroes the holder.
  4. claim pays 100e18 of RWD to the manager.
  5. The caller sweeps it. The holder still holds 0 of the reward token.

Diagrams#

flowchart TD A["Holder has the LP and the pot is 100e18 RWD"] --> B["LP transferFrom notifies the hook"] B --> C["VULN: poolUserLiquidity moves onto the pool manager"] C --> D["claim pays the reward token to the manager"] D --> E["Caller sweeps 100e18 RWD and the holder is paid 0"]

Impact#

The caller receives 100e18 of the reward token. The LP holder receives 0. The control rejects the recycled token, the liquidity stays with the holder, and the holder is paid the 100e18.

Remediation#

Reject notifyTransfer from the incentivized LP, or wrap that token before it is used as liquidity somewhere else.

How to reproduce#

BASH
cd 61562-rewards-can-be-stolen-when-incentivizederc20-tokens-are-recu_exp
forge test -vv

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.