On October 27, someone bought my BAYC #9765 for 6.23 ETH and sold it about two hours later for 6.01 WETH, receiving 5.9499 WETH after marketplace fees. I had already claimed its Mudwater allocation, so I followed the transactions to understand what happened in between.
The buyer used a vulnerability in ApeStrategy’s contract to cycle BAYC NFTs out of its pool, claim their Mudwater allocations, and exchange them for the next ape. 9765 served as a reusable asset in that process.
Purchase:
etherscan.io/tx/0x097ed13c0e…
The buyer also purchased BAYC 5611 for 6.25 ETH and interacted with the pool through a self-deployed contract named Arb.
The first successful exchange makes the mechanism clear. Before the transaction, the buyer held 9765 and 5611, while the pool held 1012. Afterward, the buyer held 9765 and 1012, and the pool held 5611. The buyer then redeemed 1012’s allocation of 10 Mudwater packs.
Next, the already-claimed 1012 went back into the pool in exchange for 1380. After claiming that allocation, the buyer moved on to 1561, and continued. 9765 repeatedly moved in and out as the supporting asset.
First exchange:
etherscan.io/tx/0xba21a8e4d0…
So where was the vulnerability?
ApeStrategy’s buyTargetNFT function accepts a caller-supplied target and calldata, then executes the external call from the pool itself. Afterward, it checks that the pool’s BAYC balance increased by one and that the specified expectedId now belongs to the pool.
Those conditions do not establish that the pool retained its existing NFTs.
If one ape leaves and two enter during the external call, the net balance change is still +1. If one of the incoming apes is expectedId, both checks pass—even though an existing asset has been removed.
The call’s authority matters too. Because the pool executes the external call, a call to the BAYC contract runs with the pool as the caller. The buyer’s Arb contract used the NFT receiver callback to coordinate the transfers. 9765 temporarily entered the pool to satisfy the checks and was subsequently recovered.
There was also an accounting component. The pool calculates an NFT’s purchase cost from the decrease in its ETH balance across the external call, then uses that cost to set its resale price.
During the callback, Arb invoked processTokenTwap, triggering a separate ETH expenditure inside that accounting window. That expenditure affected the apparent NFT purchase cost. The actual events recorded 9765 with a purchase cost of 6 wei and a resale price of 7 wei, after which it was recovered.
The buy and sell functions had nonReentrant protection, but this path did not require recursively entering the same purchase function. The external call could move NFTs, while the callback could reach processTokenTwap and affect the balance used for accounting.
Pool contract:
etherscan.io/address/0x9EbF9…
Buyer’s Arb contract:
etherscan.io/address/0xa395b…
Across the sequence, the buyer completed 21 successful Mudwater redemptions, burning 10 Totems each time: allocations corresponding to 210 packs across 21 different BAYCs.
In the final exchange, 9573 returned to the pool and the buyer recovered the original 5611. The buyer then sold both 9765 and 5611.
Final exchange:
etherscan.io/tx/0x7088008b2f…
Summing the BAYC transfers across these operations, the pool’s net change for every token ID was zero. The original apes were back, but their Mudwater entitlements had been consumed. This does not mean every other piece of contract state was restored.
The two initial apes cost 12.48 ETH and returned 12.0384 WETH after marketplace fees—a trading loss of 0.4416 ETH, before gas and claim costs. I verified the Ethereum-side redemptions, not the packs’ eventual Solana-side delivery or sale proceeds, so I am not claiming a final profit figure.
The evidence points to an exploit of ApeStrategy’s external-call and asset-accounting logic. It does not show repeated Mudwater claims for the same ape.
@0xQuit,
@Rhynotic this is a concrete NFT custody failure mode: someone can temporarily obtain an NFT, consume its one-time entitlement, and return the original NFT. The final inventory can contain exactly the same token IDs while the economic rights attached to them have changed.
This poor BAYC #9765: