PointFarm Reward Reentrancy
Exploit Transactions
0xc42fe1ce2516e125a386d198703b2422aa0190b25ef6a7b0a1d3c6f5d199ffadVictim Addresses
0xd3c41c85be295607e8ea5c58487ec5894300ee67Ethereum0xcdcc535503cba9286489b338b36156b4b75008f6EthereumLoss Breakdown
Similar Incidents
BatchSwap Counterpart Reentrancy
33%Curve Vyper Lock Reentrancy
33%Conic ETH Oracle Reentrancy
33%Cream Finance cAmp / Amp Reentrancy Exploit
31%WIFStaking claimEarned bug enables repeated WIF reward extraction
31%SorraV2 staking withdraw bug enables repeated SOR reward drain
30%Root Cause Analysis
PointFarm Reward Reentrancy
1. Incident Overview TL;DR
Unicly PointFarm and PointShop were exploited in Ethereum mainnet transaction 0xc42fe1ce2516e125a386d198703b2422aa0190b25ef6a7b0a1d3c6f5d199ffad at block 18149402. An attacker-controlled helper contract used the ERC1155 receiver callback to reenter PointFarm while rewards were being minted, causing the same pending reward tranche to be minted three times. The inflated point balance was then used to redeem LootRealms token 4689 from PointShop, after which the attacker unwound the previously staked Converter position and realized ETH.
The root cause is a checks-effects-interactions violation in PointFarm. In both deposit(uint256,uint256) and withdraw(uint256,uint256), PointFarm mints ERC1155 rewards before committing user.rewardDebt, so a contract recipient can reenter and claim the same pending reward repeatedly.
2. Key Background
Converter uTokens at 0xa499648fd0e80fd911972bbeb069e4c20e68bf22 are ERC20 claims over NFT-backed collections. Users can stake those uTokens in PointFarm at 0xd3c41c85be295607e8ea5c58487ec5894300ee67 to accrue ERC1155 point rewards. PointShop at 0xcdcc535503cba9286489b338b36156b4b75008f6 prices NFTs in those ERC1155 points and redeems an item by burning the required points and transferring the NFT to the redeemer.
The critical technical fact is that ERC1155 _mint performs an acceptance callback when the recipient is a contract. That callback reaches onERC1155Received, which is an external interaction. If reward-accounting state has not been committed before _mint, the recipient contract can reenter protocol functions while PointFarm still sees the old user.amount, pool.accPointsPerShare, and user.rewardDebt state.
The specific shop item targeted here was PointShop entry 0 for Converter shop id 0, which mapped to LootRealms token 4689 priced at 10000 points.
3. Vulnerability Analysis & Root Cause Summary
The vulnerability is an attack-class accounting bug in PointFarm reward distribution, not a market-based MEV opportunity. PointFarm calculates pending rewards as user.amount * accPointsPerShare / 1e18 - user.rewardDebt, then immediately mints ERC1155 points to msg.sender. Because _mint invokes onERC1155Received for contract recipients, the recipient can reenter PointFarm before user.rewardDebt is updated. That reentrant call recomputes the same pending reward from the same state and mints it again.
The violated invariant is: for any user and pool, a pending reward tranche may be minted at most once for a given (user.amount, pool.accPointsPerShare, user.rewardDebt) state transition. The concrete breakpoint is the branch if (pending > 0) { _mint(msg.sender, _pid, pending, data); } executing before user.rewardDebt = user.amount.mul(pool.accPointsPerShare).div(1e18); in both PointFarm.deposit and PointFarm.withdraw.
This is exploitable by any unprivileged actor that can deploy an ERC1155 receiver contract, acquire a stake in the pool, wait until pending rewards accrue, and then call the public protocol entrypoints. No privileged keys, compromised roles, flash loans, or attacker-private artifacts are required.
4. Detailed Root Cause Analysis
At the start of the exploit transaction, the attacker already had a staked Converter position inside PointFarm through helper contract 0x9d9820f10772ffcef842770b6581c07a97fed9e4. The relevant pre-state was Ethereum mainnet immediately before tx 0xc42fe1ce2516e125a386d198703b2422aa0190b25ef6a7b0a1d3c6f5d199ffad, with PointFarm pool 0, PointShop entry 0, and the helper's stake all live.
When the helper called PointFarm.deposit(0, 0), PointFarm computed a pending reward of 3528 points and minted it to the helper contract. The seed trace then shows the helper's onERC1155Received callback firing and immediately reentering deposit(0, 0) twice more before reward accounting was committed. Each frame minted the same 3528 pending reward again.
Representative trace evidence from the collected seed trace:
0xd3C41c85...::deposit(0, 0)
emit TransferSingle(..., param3: 0, param4: 3528)
0x9d9820F1...::onERC1155Received(...)
0xd3C41c85...::deposit(0, 0)
emit TransferSingle(..., param3: 0, param4: 3528)
0x9d9820F1...::onERC1155Received(...)
0xd3C41c85...::deposit(0, 0)
emit TransferSingle(..., param3: 0, param4: 3528)
The same trace records the point balance moving from 0 to 3528, then 7056, then 10584. That is the exact manifestation of the broken invariant: one legitimate pending tranche was claimed three times from a single state transition.
Once the helper held 10584 points, PointShop trusted that balance as spendable. The trace shows PointShop burning 10000 points through PointFarm and transferring LootRealms token 4689 to the helper:
0xd3C41c85...::burn(0x9d9820F1..., 0, 10000)
emit TransferSingle(..., param3: 0, param4: 10000)
LootRealms::transferFrom(0xcDCc5355..., 0x9d9820F1..., 4689)
After redeeming the NFT, the helper withdrew its staked Converter, swapped the recovered Converter for WETH through UnicSwap pair 0xec5100ad159f660986e47afa0cda1081101b471d, unwrapped WETH to ETH, and forwarded ETH plus the NFT to attacker EOA 0x92cfcb70b2591ceb1e3c6d90e21e8154e7d29832.
The balance diff confirms the realized ETH gain. The attacker EOA's ETH balance moved from 313566780000000000 wei to 806342047021571153 wei, a net increase of 492775267021571153 wei after paying 4101682063480222 wei in gas. Separately, WETH lost 496876949085051375 wei, matching the gross ETH forwarded from the helper after unwinding the recovered Converter stake.
5. Adversary Flow Analysis
The adversary cluster contains EOA 0x92cfcb70b2591ceb1e3c6d90e21e8154e7d29832 and helper contract 0x9d9820f10772ffcef842770b6581c07a97fed9e4. The EOA is the seed transaction sender and final recipient of value. The helper is the execution contract that reenters PointFarm, holds the inflated points, redeems the NFT, performs the unwind, and forwards assets out.
The exploit path is:
- Maintain a positive stake of Converter uTokens in PointFarm pool
0. - Wait until
pendingPoints(0, helper)is positive but still below the10000-point PointShop price. - Call
deposit(0, 0)from a contract implementingonERC1155Received. - Reenter
deposit(0, 0)from the callback while PointFarm still has stale reward-accounting state. - Repeat until the helper's point balance exceeds the PointShop price.
- Burn
10000points via PointShopredeem(address,uint256)to obtain LootRealms token4689. - Withdraw the previously staked Converter, swap it for WETH, unwrap to ETH, and forward assets to the attacker EOA.
This sequence is fully permissionless. The exploit relies only on public contracts, public calldata, and standard EVM callback behavior.
6. Impact & Losses
The direct accounting loss in the root-cause artifact is 10584 forged ERC1155 points with decimal=0. Those points were sufficient to bypass PointShop pricing and empty the targeted shop entry for LootRealms token 4689.
The transaction also realized economic gain for the attacker. The helper forwarded 496876949085051375 wei gross to the attacker EOA, and the EOA's balance increased by 492775267021571153 wei net after gas. The seed transaction therefore combined protocol-state corruption, unauthorized NFT redemption, and positive ETH extraction in one transaction.
7. References
- Seed transaction metadata for tx
0xc42fe1ce2516e125a386d198703b2422aa0190b25ef6a7b0a1d3c6f5d199ffad - Seed opcode trace showing nested
deposit(0,0)calls, point mints, point burn, and NFT transfer - Seed balance diff confirming attacker EOA net ETH gain and Converter/WETH movements
- Converter verified source at
0xa499648fd0e80fd911972bbeb069e4c20e68bf22 - LootRealms verified source at
0x7afe30cb3e53dba6801aa0ea647a0ecea7cbe18d - PointFarm verified source at
0xd3c41c85be295607e8ea5c58487ec5894300ee67 - PointShop verified source at
0xcdcc535503cba9286489b338b36156b4b75008f6