AirdropGrapesToken ApeCoin Claim via NFTX BAYC Vault
Exploit Transactions
Victim Addresses
0x025c6da5bd0e6a5dd1350fda9e3b6a614b205a1fEthereum0xea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5EthereumLoss Breakdown
Similar Incidents
NFTX Doodles collateral accounting flaw enables flash-loan ETH extraction
37%P2Controller/XNFT BAYC Over-Borrow via Per-Order-Only Collateral
35%Revest TokenVault withdrawFNFT accounting flaw drains RENA vault reserves
32%FlippazOne ungated ownerWithdrawAllTo lets attacker drain 1.15 ETH
30%PumpToken removeLiquidityWhenKIncreases Uniswap LP Drain
29%AnyswapV4Router WETH9 permit misuse drains WETH to ETH
29%Root Cause Analysis
AirdropGrapesToken ApeCoin Claim via NFTX BAYC Vault
1. Incident Overview TL;DR
An adversary-controlled helper stack on Ethereum mainnet — EOA 0x6703741e913a30d6604481472b6d81f3da45e6e8 together with helper contracts 0x3ebd3d86f810b141f9b2e9b15961fc66364b54f3 and 0x7797a99a2e91646abdc9dc30e838a149ccb3013b — exploited the AirdropGrapesToken ApeCoin airdrop contract 0x025c6da5bd0e6a5dd1350fda9e3b6a614b205a1f.
Within a single transaction 0xeb8c3bebed11e2e4fcd30cbfc2fb3c55c4ca166003c7f7d319e78eaab9747098 in block 14403949, the adversary temporarily accumulated on-chain ownership of six BAYC NFTs (IDs 1060, 7594, 8214, 9915, 8167, 4755), five of which belonged economically to depositors in the NFTX BAYC vault 0xea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5. While holding those NFTs, the helper contract 0x7797a9… called AirdropGrapesToken.claimTokens(), causing the contract to treat 0x7797a9… as the owner of those BAYCs and transfer 60,564 ApeCoin to the adversary cluster. The NFTs were then returned to the vault, leaving vault depositors unable to claim their ApeCoin allocation.
At a high level, the root cause is that AirdropGrapesToken.claimTokens() grants claim rights based solely on the live ERC721 owner at claim time (via ownerOf/tokenOfOwnerByIndex), with no binding to a snapshot owner or underlying economic owner behind vault wrappers. Any unprivileged actor who can temporarily take custody of eligible BAYC/MAYC/BAKC tokens can therefore permanently consume the associated ApeCoin allocations.
2. Key Background
AirdropGrapesToken (0x025c6d…) is a generic airdrop contract deployed on Ethereum mainnet to distribute ApeCoin (0x4d224452801aced8b2f0aebe155379bb5d594381) to holders of BAYC, MAYC, and BAKC NFTs. The constructor binds:
grapesTokento ApeCoin (ERC20)0x4d2244…,alphato BAYC (ERC721Enumerable)0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d,betato MAYC0x60e4d786628fea6478f785a6d7e704777c86a7c6,gammato BAKC0xba30e5f9bb24caa003e9f2f0497ad287fdf95623.
The contract holds a fixed ApeCoin pool and defines per-token distribution amounts for each collection. Eligibility is enforced at the token ID level via mappings alphaClaimed, betaClaimed, and gammaClaimed.
The key claim function is:
// Seed: verified source for 0x025c6d… (AirdropGrapesToken.sol)
function claimTokens() external whenNotPaused {
require(block.timestamp >= claimStartTime && block.timestamp < claimStartTime + claimDuration, "Claimable period is finished");
require((beta.balanceOf(msg.sender) > 0 || alpha.balanceOf(msg.sender) > 0), "Nothing to claim");
uint256 tokensToClaim;
uint256 gammaToBeClaim;
(tokensToClaim, gammaToBeClaim) = getClaimableTokenAmountAndGammaToClaim(msg.sender);
for (uint256 i; i < alpha.balanceOf(msg.sender); ++i) {
uint256 tokenId = alpha.tokenOfOwnerByIndex(msg.sender, i);
if (!alphaClaimed[tokenId]) {
alphaClaimed[tokenId] = true;
emit AlphaClaimed(tokenId, msg.sender, block.timestamp);
}
}
// Analogous loops for beta and gamma, then:
grapesToken.safeTransfer(msg.sender, tokensToClaim);
totalClaimed += tokensToClaim;
emit AirDrop(msg.sender, tokensToClaim, block.timestamp);
}
This implementation determines both eligibility and the payout recipient purely from the current ERC721Enumerable view of msg.sender’s holdings at claim time. There is no reference to:
- the owner at a prior snapshot block,
- the address that bore the economic risk of the NFTs (e.g., NFTX vault depositors), or
- any off-chain allowlist.
The NFTX BAYC vault 0xea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5 is a separate protocol that holds BAYC NFTs on-chain and issues fungible ERC20 vault tokens representing fractional ownership. At blocks 14403948 and 14403949, the vault owns BAYC token IDs {7594, 8214, 9915, 8167, 4755} while depositors hold the corresponding vault ERC20 shares:
// BAYC owner snapshots at blocks 14403948 and 14403949
{
"token_id": 7594,
"owner_at": {
"14403948": { "result": "0x000000000000000000000000ea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5" },
"14403949": { "result": "0x000000000000000000000000ea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5" }
}
}
Thus, at the time of the exploit, the canonical on-chain owner for five of the six BAYCs is the NFTX vault contract, not the vault depositors.
3. Vulnerability Analysis & Root Cause Summary
Vulnerability class. The incident arises from a mismatch between the intended economic entitlement model (ApeCoin allocations belong to BAYC/MAYC/BAKC holders at the beginning of the claim period, including those using vault wrappers) and the actual on-chain entitlement check, which uses the current ERC721 owner at claim time. This is a “live ownership over snapshot/economic ownership” vulnerability in an airdrop contract.
Root cause summary. AirdropGrapesToken.claimTokens() computes tokensToClaim and marks token IDs as claimed exclusively by:
- enumerating the current tokens owned by
msg.senderviatokenOfOwnerByIndex(msg.sender, i), and - checking whether each token ID has not yet been marked as claimed.
There is no binding between the ApeCoin allocation for a given BAYC/MAYC/BAKC token ID and the account that held that token at the beginning of the claim period or that bears its economic risk. Any address that can route those NFTs through itself during the claim window is treated as the legitimate claimer and permanently consumes the associated allocation.
Invariant and breakpoint.
- Intended invariant. For each BAYC/MAYC/BAKC token ID, only the account that is intended to hold the economic rights to that NFT at the start of the claim period should be able to claim its associated ApeCoin allocation; third parties must not be able to permanently seize another party’s allocation merely by taking temporary custody of the NFT.
- Breakpoint. In
claimTokens(), the contract bases eligibility and payout solely on livealpha.balanceOf(msg.sender)/alpha.tokenOfOwnerByIndex(msg.sender, i)(and analogs forbeta/gamma) against the current ERC721 state, markingalphaClaimed[tokenId] = trueand transferring ApeCoin tomsg.sender. This logic allows any address that temporarily holds eligible NFTs at claim time to consume their allocations, regardless of underlying vault or wrapper relationships.
Because all relevant contract code and on-chain traces are public, this behavior constitutes an Anyone-Can-Take (ACT) opportunity for unprivileged adversaries who can engineer temporary control of eligible NFTs.
4. Detailed Root Cause Analysis
This section links the vulnerability to the concrete on-chain execution that produced the loss.
4.1. Pre-state and ownership configuration (σ_B)
At block 14403948 (immediately before the profit transaction’s block), the relevant state is:
- BAYC ownership snapshots show:
- Token ID
1060owned by adversary EOA0x6703741e913a30d6604481472b6d81f3da45e6e8. - Token IDs
7594,8214,9915,8167,4755owned by NFTX BAYC vault0xea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5.
- Token ID
- AirdropGrapesToken
0x025c6d…holds a large ApeCoin balance with per-token allocations preconfigured for BAYC/MAYC/BAKC and maintainsalphaClaimed,betaClaimed,gammaClaimedmappings to prevent double-claims. - Helper contracts
0x3ebd3d…and0x7797a9…are not yet deployed.
Snapshot evidence:
// Owner snapshots for BAYC token IDs 1060 and 7594 at 14403948 and 14403949
{
"token_id": 1060,
"owner_at": {
"14403948": { "result": "0x0000000000000000000000006703741e913a30d6604481472b6d81f3da45e6e8" },
"14403949": { "result": "0x000000000000000000000000ea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5" }
}
}
Normal and internal txlists for the NFTX vault and helpers show that the vault’s BAYC holdings were established prior to the incident by third-party deposits and that 0x3ebd3d… and 0x7797a9… are single-use contracts created and used only in the profit transaction.
4.2. Airdrop claim logic and its implications
Combining the AirdropGrapesToken source and its deployment metadata:
claimTokens()callsgetClaimableTokenAmountAndGammaToClaim(msg.sender)to computetokensToClaimby iterating over all token IDs currently held by_accountand counting those not yet marked as claimed.- For each unclaimed BAYC (
alpha) token owned bymsg.sender, the contract:- Marks
alphaClaimed[tokenId] = true, - Emits
AlphaClaimed(tokenId, msg.sender, timestamp).
- Marks
- After processing BAYC, MAYC, and BAKC tokens similarly, the contract transfers
tokensToClaimApeCoin tomsg.sender.
There is no parameterization of claimTokens() by token IDs or owner at a particular block; the on-chain logic solely trusts the dynamic ERC721 Enumerable views. Therefore, if an attacker can:
- Temporarily cause multiple eligible BAYCs to be owned by an adversary-controlled contract
Aduring the claim period, and - Call
claimTokens()asA,
then the contract will:
- Treat
Aas the legitimate claimant for all those BAYCs, - Permanently mark those token IDs as claimed, and
- Transfer the associated ApeCoin allocations to
A(and ultimately back to the adversary EOA).
This is the exact pattern realized in tx 0xeb8c3b…7098.
4.3. Helper/orchestrator design
The adversary deploys an orchestrator contract at 0x7797a99a2e91646abdc9dc30e838a149ccb3013b. The decompiled code shows:
// Seed: Heimdall decompile for 0x7797a9… (excerpt)
contract DecompiledContract {
address public apex; // NFTX BAYC vault token
address public sushiswap; // DEX router
address public apecoin; // ApeCoin token
address public weth; // WETH
address public claims; // AirdropGrapesToken
address public bayc; // BAYC
function Unresolved_b61d27f6(address arg0, uint256 arg1, uint256 arg2) public payable {
// ...
require(address(msg.sender) == 0x6703741e913a30d6604481472b6d81f3da45e6e8);
// forwards value to arg0, used for routing ETH/APE back to the EOA
}
function apein() public payable {
// interacts with apex (NFTX vault token), sushiswap, apecoin, weth
// routes proceeds to 0x6703…e6e8
}
}
This structure establishes that:
0x7797a9…is not a generic third-party protocol; it is a bespoke orchestrator controlled by EOA0x6703…e6e8.- The orchestrator coordinates NFTX vault interactions, ApeCoin transfers, and DEX swaps, with value flows terminating at the EOA.
4.4. Concrete exploit sequence
The exploit consists of two EOA-initiated transactions:
-
Priming transaction
0x8a61927598ce26f296ba1be155301418e2c01fe8f5585f9b46369e1ec5c408b0(block14403831):- From: EOA
0x6703…e6e8. - Calls
BAYC.setApprovalForAll(0x3ebd3d86f810b141f9b2e9b15961fc66364b54f3, true). - Effect: Grants helper contract
0x3ebd3d…permission to move the EOA’s BAYC NFTs, including token ID1060.
- From: EOA
-
Attack/profit transaction
0xeb8c3bebed11e2e4fcd30cbfc2fb3c55c4ca166003c7f7d319e78eaab9747098(block14403949):- From: EOA
0x6703…e6e8(nonce 3, value 0). - Actions within the single transaction (from traces and receipts):
- Deploys helper contract
0x3ebd3d…. - Uses
0x3ebd3d…plus the prior approval to move BAYC token1060from the EOA into orchestrator0x7797a9…. - Deploys orchestrator
0x7797a9…. - Interacts with NFTX vault
0xea47…to withdraw (or otherwise route) BAYC IDs7594,8214,9915,8167,4755such that0x7797a9…temporarily becomes their owner. - Calls
AirdropGrapesToken.claimTokens()from0x7797a9…, causing the airdrop contract to:- Enumerate the six BAYC token IDs owned by
0x7797a9…viaalpha.tokenOfOwnerByIndex(0x7797a9…, i), - Mark those IDs as claimed,
- Transfer
60,564ApeCoin from0x025c6d…to0x7797a9….
- Enumerate the six BAYC token IDs owned by
- Forwards the ApeCoin and additional ETH proceeds to the EOA via orchestrator functions and DEX swaps.
- Returns the five NFTX-origin BAYCs to the NFTX vault so that on-chain holdings at
14403949again show the vault as owner of those five tokens, and BAYC1060also ends in the vault.
- Deploys helper contract
- From: EOA
The net effect is that NFTX vault depositors lose the right to claim ApeCoin via 0x025c6d… for their BAYC token IDs, while the adversary cluster captures that ApeCoin and additional ETH.
5. Adversary Flow Analysis
This section details the adversary-related cluster and the on-chain flow realizing the ACT opportunity.
5.1. Adversary-related cluster
Evidence from traces, txlists, and decompiled code supports the following adversary cluster:
- EOA:
0x6703741e913a30d6604481472b6d81f3da45e6e8- Initiates both the priming tx and the profit tx.
- Receives ApeCoin and ETH proceeds at the end of the exploit.
- Helper contract 1:
0x3ebd3d86f810b141f9b2e9b15961fc66364b54f3- Deployed by the EOA in the profit tx.
- Uses
setApprovalForAllfrom the prior priming tx to move BAYC1060. - No additional activity outside the exploit transaction.
- Orchestrator contract:
0x7797a99a2e91646abdc9dc30e838a149ccb3013b- Deployed and used only within the profit tx.
- Decompile reveals routing of ApeCoin and ETH to
0x6703…e6e8and an explicitrequire(msg.sender == 0x6703…e6e8)gate in a key function.
No evidence indicates that NFTX vault 0xea47…, DEX router, or other external contracts are controlled by the adversary; they act as permissionless liquidity and wrapper infrastructure.
5.2. ACT opportunity and inclusion feasibility
The entire strategy fits the ACT model:
- Pre-state: Publicly observable state at block
14403948, including BAYC ownership and AirdropGrapesToken’s balances and configuration. - Strategy:
- Acquire BAYC exposure (here via prior deposits and NFTX interactions).
- Ensure the ability to move BAYC NFTs (EOA obtains and grants approvals).
- Use helper contracts to temporarily route BAYC NFTs into an adversary-controlled address.
- Call
AirdropGrapesToken.claimTokens()from that address. - Return NFTs and realize ApeCoin/ETH profit.
- Inclusion feasibility: Both txs (
0x8a61…408b0and0xeb8c3b…7098) are standard EOA transactions with normal gas usage, no privileged roles, and only calls to publicly deployed contracts with public ABIs or decompiled code. Any unprivileged searcher with comparable on-chain state and code access could construct equivalent calldata. - Deterministic success predicate: The profit condition is purely on-chain: the adversary cluster’s ApeCoin balance increases by
60,564APE, and there is no ApeCoin outflow from the cluster in the same tx.
5.3. Profit computation
The balance_diff.json artifact for tx 0xeb8c3b…7098 shows:
// Seed: balance_diff.json for tx 0xeb8c3b…7098 (APE deltas)
{
"erc20_balance_deltas": [
{
"token": "0x4d224452801aced8b2f0aebe155379bb5d594381",
"holder": "0x025c6da5bd0e6a5dd1350fda9e3b6a614b205a1f",
"before": "150000000000000000000000000",
"after": "149939436000000000000000000",
"delta": "-60564000000000000000000"
},
{
"token": "0x4d224452801aced8b2f0aebe155379bb5d594381",
"holder": "0x6703741e913a30d6604481472b6d81f3da45e6e8",
"before": "0",
"after": "60564000000000000000000",
"delta": "60564000000000000000000"
}
]
}
- ApeCoin (token
0x4d2244…) decreases by60,564APE at the airdrop contract and increases by the same amount at the adversary EOA. - No other ApeCoin holders change balance in this transaction.
Native ETH/WETH deltas also show a net ETH gain for the EOA mediated via WETH and DEX interactions, but the ACT profit predicate can be stated purely in ApeCoin units:
- Reference asset: ApeCoin (APE), 18 decimals.
- Adversary cluster initial ApeCoin balance:
0. - Adversary cluster final ApeCoin balance:
60,564APE. - Net APE profit:
60,564APE.
Gas and ancillary costs are paid in ETH and do not diminish the ApeCoin-denominated profit predicate.
6. Impact & Losses
Direct on-chain loss.
- AirdropGrapesToken
0x025c6d…transfers60,564ApeCoin from its reserves to the adversary EOA0x6703…e6e8in tx0xeb8c3b…7098. - This ApeCoin corresponds to the per-token allocation for the six BAYC NFTs
{1060, 7594, 8214, 9915, 8167, 4755}as encoded in AirdropGrapesToken’s distribution parameters.
Economic impact to stakeholders.
- Five of the six BAYC NFTs (
7594,8214,9915,8167,4755) are owned by the NFTX vault0xea47…both before and after the tx, as shown by BAYC owner snapshots. - Those NFTs are economically owned by NFTX vault depositors who hold vault ERC20 shares; they never control
0x7797a9…or initiate the claim. - After the exploit, AirdropGrapesToken’s mappings mark all six BAYC token IDs as claimed. The vault depositors are therefore permanently unable to claim ApeCoin for their BAYCs via
0x025c6d…, and the value is captured by the adversary cluster.
The precise USD-denominated loss depends on external market prices for ApeCoin and BAYC at the time, but on-chain data deterministically records a transfer of 60,564 APE from the airdrop contract to the adversary cluster and the irreversible consumption of the corresponding claim rights for the affected BAYC token IDs.
7. References
-
Seed transaction and core artifacts
- Ethereum mainnet tx
0xeb8c3bebed11e2e4fcd30cbfc2fb3c55c4ca166003c7f7d319e78eaab9747098(attacker-profit). artifacts/root_cause/seed/1/0xeb8c3bebed11e2e4fcd30cbfc2fb3c55c4ca166003c7f7d319e78eaab9747098/metadata.json.artifacts/root_cause/seed/1/0xeb8c3bebed11e2e4fcd30cbfc2fb3c55c4ca166003c7f7d319e78eaab9747098/balance_diff.json.artifacts/root_cause/seed/1/0xeb8c3bebed11e2e4fcd30cbfc2fb3c55c4ca166003c7f7d319e78eaab9747098/trace.cast.log.
- Ethereum mainnet tx
-
AirdropGrapesToken contract
- Contract:
0x025c6da5bd0e6a5dd1350fda9e3b6a614b205a1f(AirdropGrapesToken). - Verified source and deployment history:
artifacts/root_cause/data_collector/iter_2/contract/1/0x025c6da5bd0e6a5dd1350fda9e3b6a614b205a1f/.
- Contract:
-
NFTX BAYC vault and BAYC ownership
- NFTX vault:
0xea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5(NFTX BAYC vault). - Logs and tx history:
artifacts/root_cause/data_collector/iter_3/contract/1/0xea47b64e1bfccb773a0420247c0aa0a3c1d2e5c5/. - BAYC owner snapshots for token IDs
{1060, 7594, 8214, 9915, 8167, 4755}at blocks14403948and14403949:artifacts/root_cause/data_collector/iter_3/other/bayc_owner_snapshots/owners_14403948_14403949.json.
- NFTX vault:
-
Helper and orchestrator contracts
- Helper:
0x3ebd3d86f810b141f9b2e9b15961fc66364b54f3(created in the attacker-profit tx). - Orchestrator:
0x7797a99a2e91646abdc9dc30e838a149ccb3013b. - Decompile and ABI:
artifacts/root_cause/data_collector/iter_2/contract/1/0x7797a99a2e91646abdc9dc30e838a149ccb3013b/decompile/. - Normal and internal txlists for helpers across analysis iterations, confirming single-use attack behavior.
- Helper:
-
Adversary EOA history
- EOA:
0x6703741e913a30d6604481472b6d81f3da45e6e8. - Normal and internal txlists:
artifacts/root_cause/data_collector/iter_1/address/1/0x6703741e913a30d6604481472b6d81f3da45e6e8/. - Priming tx
0x8a61927598ce26f296ba1be155301418e2c01fe8f5585f9b46369e1ec5c408b0callingBAYC.setApprovalForAll(0x3ebd3d…, true).
- EOA: