# JAY token: reentrancy via malicious ERC721 transferFrom drains pool

JAY token's buyJay accepted an attacker-controlled ERC721 whose transferFrom reentered sell(), letting the attacker inflate and dump JAY for ~15.32 ETH profit.

- Payout: Real-world hack
- Severity: high
- Program: - JAY
- Analysis by: DeFiHackLabs
- Disclosed: 2022-12-29
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-12/JAY_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20221229-jay
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:chained vuln:business-logic vuln:flash-loan vuln:reentrancy

**Target:** JAY token buy/sell contract on Ethereum mainnet
**Root cause:** buyJay() calls transferFrom on a caller-supplied ERC721 address without validating it, and updates accounting after the external call, so a malicious token can reenter sell() mid-purchase.
**Steps:**
1. Flash-borrow 72.5 WETH from Balancer Vault, withdraw to ETH.
2. Call `buyJay{value: 22 ether}([], [], [], [], [])` to seed JAY balance.
3. Call `buyJay{value: 50.5 ether}([attackerContract], [0], [], [], [])` where attackerContract.transferFrom reenters `JAY_TOKEN.sell(JAY_TOKEN.balanceOf(address(this)))`.
4. Sell remaining JAY balance via `sell(balanceOf(this))`.
5. Repeat with `buyJay{value: 3.5 ether}` then `buyJay{value: 8 ether}([attackerContract],[0],...)` triggering reentrant sell again, then final `sell`.
6. Redeposit 72.5 ETH to WETH and repay Balancer flash loan.
**Exploitation:** Attacker flash-borrows 72.5 WETH, converts to ETH, and repeatedly buys JAY while passing a malicious ERC721 contract as the payment token. During buyJay's transferFrom callback the attacker reenters sell(), selling JAY at an inflated price before the buy accounting settles, then dumps the rest. Net ~15.32 ETH profit after repaying the flash loan.
**Insight:** When a buy/sell function accepts arbitrary token addresses for payment, treat the transferFrom callback as an attacker-controlled reentrancy hook: check whether state (balances, reserves) is updated before or after that external call.

