Table of contents
Introduction
TheIdolsNFT Incident Due to Mismanagement of Identical Address Pair Parameters
Alien Base Dex Incident from a Sell-AddLiquidity-Buy Sandwich Attack
Exploitation of ERC-6492 Arbitrary Call via Precompile Contract
Introduction
Welcome to The Notorious Bug Digest, a short collection of insights about recent Web3 bugs and security incidents! When not auditing, some of our security researchers use their time to keep up-to-date with the security space, read bug reports and study on-chain incidents. We think such information is useful to the security community, and can help other researchers hone their skills or onboard to the security space. Hence, we decided to share this content externally.
TheIdolsNFT Incident Due to Mismanagement of Identical Address Pair Parameters
The NFT project features a yield and claim mechanism, as illustrated in Picture A. rewardPerGod
tracks the cumulative amount of stETH
awarded per NFT since the protocol began. claimedSnapshots
is a mapping that records how much reward each user has claimed for each NFT. When users claim their rewards, the _claimEthRewards
function calculates the rewards based on the number of NFTs held by the user and the difference between rewardPerGod
and the user's claimedSnapshots
. After calculation, it updates the user's claimedSnapshots
to match rewardPerGod
.
As shown in Picture B, during a token transfer operation, the _beforeTokenTransfer
hook automatically claims rewards for both the sender and receiver. If the sender ends up holding no NFTs after the transfer, their claimedSnapshots
are reset to zero. However, when the sender and receiver are the same address, and they only hold one NFT, the receiver's claimedSnapshots
are reset to zero before the rewards are claimed, allowing the receiver to claim more rewards.
Picture C illustrates how the attacker exploited this vulnerability by repeatedly transferring a single NFT between the same sender and receiver address, thus accumulating undue profits.
Alien Base Dex Incident from a Sell-AddLiquidity-Buy Sandwich Attack
As illustrated in Picture A, the contract features a compound
function meant to collect fees from a liquidity pool and reinvest them. However, this function is permissionless; the tick range is determined by the caller, and there's no slippage protection (amountMin = 0
). Picture B outlines the attack sequence: The attacker begins by selling flash-loaned ALB tokens, which drives down the ALB/WETH price. They then trigger the compound
function, adding liquidity at this artificially low price. Following this, the attacker repurchases ALB with WETH, taking advantage of the increased liquidity at the lower price to end up with more ALB than they started with. Finally, the attacker converts these ALB tokens back into WETH in another pool to secure their profits.

Exploitation of ERC-6492 Arbitrary Call via Precompile Contract
A project using ERC-6492 was hacked, resulting in a loss of $50k. While the root cause—an arbitrary call—is straightforward, the exploitation was interesting as it used a precompile contract to bypass normal interface calls. ERC-6492 extends ERC-1271 by allowing signature validation for contracts not yet deployed, known as counterfactual contracts. The process involves wrapping signatures with additional deployment data when the contract isn't on-chain, using a format like concat(abi.encode((create2Factory, factoryCalldata, originalSignature)), magicBytes)
, where magicBytes
(0x6492...6492
) signal this special verification. Verifiers first check for these magic bytes to see if deployment is needed before validation. If the contract is already deployed, standard ERC-1271 verification is used; otherwise, create2Factory
is called with factoryCalldata
to deploy it.
ERC-6492 also provides a reference implementation for verifiers, and the project used a simplified version. According to the code, if there's a specific suffix in the signature and the signer contract isn't deployed yet (code.length == 0
), the verifier calls create2Factory
. Note that both create2Factory
and factoryCalldata
come from an external user, which introduces an arbitrary call vulnerability. But is this actually exploitable?
Let's assume isCounterfactual
is true
and _signer.code.length == 0
, and we trigger a USDC.transfer
call to drain USDC
tokens from the verifier contract. The control flow then enters the // Try ERC-1271 verification
branch and executes try IERC1271Wallet(_signer).isValidSignature(_hash, sigToValidate) returns (bytes4 magicValue)
. Since no contract is actually deployed at _signer
's address, isValidSignature
returns empty data, causing a decode revert, and the hack fails.The first workaround involves using tokens with hooks. For instance, ERC-1155 has a receiver hook onERC1155Received
on transfer. If ERC-1155 tokens are left in the verifier contract, we could deploy a contract at _signer
that returns data for the isValidSignature
call. But what if the contract only holds ERC-20 tokens like USDC
? Here, we can't manipulate the control flow during USDC.transfer
, yet we need isValidSignature
to succeed. Is there any contract where code.length == 0
but returns >= 4 bytes when called with isValidSignature(bytes32, bytes)
?
- Contracts with
code.length == 0
are Precompiles! They don't have EVM code but Node code. - Precompiles "ignore" function selectors, focusing on raw
calldata
.
Looking at the current precompiles in EVM (Cancun), there are several potential candidates. For instance, the precompile at address 0x4 accepts bytes data and returns the exact input; since the input is larger than 4 bytes, the decoding succeeds! The attacker used this method to execute the hack.
As shown in the picture, they called isValidSigImpl
with _signer
as the 0x4 precompile, setting create2Factory
with factoryCalldata
to USDC.transfer(attacker, balance)
. This triggered the transfer and allowed the IERC1271Wallet(_signer).isValidSignature
call to execute successfully at the end.
Shortly after this incident, @pcaversaccio opened an issue suggesting to add warnings about potential vulnerabilities to the official ERC-6492 reference implementation, including the use of precompile addresses (0x04) in combination with arbitrary calls.
It's important to emphasise that the intent behind this content is not to criticise or blame the affected projects, but rather provide objective overviews that serve as educational material for the community to learn from and better protect projects in the future.