BlockchainSolidity9 min readUpdated

Events in Solidity: Cheap On-Chain Logs for Off-Chain Systems

By Mudassir Khan — Agentic AI Consultant & AI Systems Architect, Islamabad, Pakistan

Cover illustration for: Events in Solidity: Cheap On-Chain Logs for Off-Chain Systems

Section 01 · Definition

What is an event in Solidity?

An event is a typed log entry that the EVM writes into a block alongside transaction execution. It is how a contract speaks to the off-chain world.

Quick answer

What is an event? An event is a structured log emitted by a contract during a transaction. It has a name, a topic hash (the keccak256 of the event signature), and a payload of typed data. Off-chain systems subscribe to events to know when something happened on chain — a token transferred, an order filled, a vote cast — without having to scan every storage slot.

solidity
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
event Paused(bool paused);

function transfer(address to, uint256 amount) external {
    balances[msg.sender] -= amount;
    balances[to]         += amount;
    emit Transfer(msg.sender, to, amount);     // write the log
}

Notice the indexed keyword on two parameters. Indexed parameters become "topics" in the log entry, which off-chain consumers can filter on directly. Up to three indexed parameters per event are allowed. Non-indexed parameters live in the data section and require parsing to extract. To compute the topic 0 hash of any event signature for off chain filters, paste it into the Keccak256 Hash Generator.

Section 02 · The pipeline

From emit to dashboard

An event leaves your contract, lands in a block, and ends up in a database — all within seconds.

Pipeline diagram showing emit, block log storage, and off chain consumers like indexers and dashboards.
Three steps from emit to indexer. The contract never sees the log again — events are output.

A typical event reaches its consumers in three hops. First the contract calls emit, which the EVM compiles to a LOG opcode. Second the log is written into the transaction receipt and stored in the block's bloom filter. Third an off-chain consumer (a Graph subgraph, an ethers.js listener, an Etherscan watcher) fetches the receipt and decodes the log against the event ABI. The contract itself never sees the log again — it is one-way output.

Events vs storage cost

Storing 32 bytes of state costs ~20 000 gas. Emitting an event with the same 32 bytes costs ~1 500 gas plus 8 per byte. That is more than ten times cheaper. Emit liberally on every state change — the cost is negligible and downstream systems get a clean stream of changes to subscribe to.

Section 03 · indexed

What the indexed keyword actually does

indexed turns a parameter into a topic that off-chain filters can match exactly. Use it on values you will look up by — addresses, IDs, status enums.

solidity
event OrderPlaced(
    uint256 indexed orderId,        // topic 1
    address indexed trader,         // topic 2
    address indexed asset,          // topic 3
    uint256 amount,                 // data
    uint256 priceWei                // data
);

emit OrderPlaced(123, msg.sender, USDC, 5_000, 1e18);

A consumer can now ask the chain for "every OrderPlaced where asset == USDC and trader == 0xAlice" in one call, with no decoding work. Without indexed they would have to fetch every OrderPlaced and filter client-side, which is much slower.

The trade-off: indexed values longer than 32 bytes (strings, dynamic bytes) are stored as a hash, not the raw value. The actual content is unrecoverable from the log alone — you would also have to fetch the original transaction. For short values like address, uint256, and bytes32, this is a non-issue.

Section 04 · Standard events

The events ERC standards require

Wallets, marketplaces, and explorers all listen for the standard event signatures. Forget to emit one and your contract will not appear correctly in tooling.

solidity
// ERC20 — must emit on every transfer and approval
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);

// ERC721 — must emit on every transfer, approval, and operator change
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

// ERC1155 — must emit on every transfer (single and batch)
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);

MetaMask, Etherscan, OpenSea, Uniswap, and the Graph subgraphs all watch for these exact signatures. If you write a transfer function but skip the emit Transfer, the balances will update on chain but no wallet will notice — the user appears to lose their tokens until they manually refresh and the wallet rescans the chain.

Section 05 · Real-world uses

Where events show up in production code

solidity
// 1. Token movements (every ERC standard)
emit Transfer(from, to, amount);

// 2. Admin changes — make every privileged action visible
event OwnerChanged(address indexed previousOwner, address indexed newOwner);
event FeeChanged(uint256 oldFee, uint256 newFee);

// 3. Lifecycle events — useful for off-chain analytics
event PositionOpened(address indexed user, uint256 collateral, uint256 debt);
event PositionLiquidated(address indexed user, address indexed liquidator, uint256 amount);

// 4. Error and warning events for monitoring
event SuspiciousActivity(address indexed actor, bytes32 reason);

Section 07 · FAQ

Frequently asked questions

Can a contract read its own events back?

No. Events are written into the transaction log and are not part of the contract's state. From inside Solidity there is no way to read past events — that is the job of off-chain systems. If a value needs to be readable on chain, store it in state.

What does indexed do?

indexed turns the parameter into a separately stored 'topic' in the log entry, which off-chain filters can match exactly without decoding the rest of the data. You can mark up to three parameters as indexed per event. Indexed dynamic types (string, bytes) are stored as a keccak256 hash, not the raw value.

How much does it cost to emit an event?

Roughly 375 gas for the LOG opcode itself plus 375 per indexed topic plus 8 per byte of data. A simple Transfer event costs around 1 500 to 2 000 gas total — about ten times cheaper than the equivalent storage write.

Why does my wallet not show a token I just received?

Almost always because the contract did not emit a Transfer event with the standard ERC signature. Wallets watch the log stream, not contract storage. Without the event, the transfer is invisible to the wallet until the next manual rescan.

Can events be removed or modified after they are emitted?

No. Events are part of the transaction receipt and are immutable like every other piece of block data. Once a transaction is included, its events cannot be changed by anyone, including the contract that emitted them.

Written by Mudassir Khan

Agentic AI consultant and AI systems architect based in Islamabad, Pakistan. CEO of Cube A Cloud. 38+ agentic AI launches delivered for global founders and CTOs.

View agentic AI consulting serviceSee ChainTrust case study

Related service

Agentic AI Consulting

See scope & pricing →

Related case study

ChainTrust Compliance Engine

Read case study →

More on this topic

Need an AI systems architect?

Book a 30-minute architecture call. I will sketch the high-level design for your use case and give you an honest view of the trade-offs.

Book a strategy call →