Docs

events

Event emission and indexing

Overview

Events in Move provide a mechanism for smart contracts to emit structured notifications about state changes to off-chain systems. Events enable indexers, applications, and users to track on-chain activities without continuously polling resources.

Technical Details

Events are defined as Move structs and emitted using the event::emit function:

move
use aptos_framework::event;

struct TransferEvent has drop, store {
    from: address,
    to: address,
    amount: u64
}

public entry fun transfer_with_event(from: &signer, to: address, amount: u64) {
    // Transfer logic here
    event::emit(TransferEvent {
        from: signer::address_of(from),
        to,
        amount
    });
}

Event Properties:

  • Stored off-chain in transaction metadata, not on-chain state
  • Ordered sequentially within each transaction
  • Identified by type and containing transaction
  • Queryable via REST API and GraphQL indexer
  • Support generic type parameters
  • Require drop and store abilities

How Events Work

When code emits an event during transaction execution:

  1. Event data is serialized and attached to the transaction
  2. Validators include events in the committed transaction output
  3. Indexers process events and make them queryable
  4. Applications query events via REST or GraphQL APIs
  5. Events provide an audit trail of contract activity

Unlike state changes that modify resources, events create an append-only log that never changes.

Practical Examples

Token Transfer Events:

move
struct CoinTransferEvent has drop, store {
    sender: address,
    receiver: address,
    amount: u64,
    coin_type: TypeInfo
}

NFT Marketplace Events:

move
struct ListingCreatedEvent has drop, store {
    seller: address,
    token_id: TokenId,
    price: u64,
    expiration: u64
}

struct PurchaseEvent has drop, store {
    buyer: address,
    seller: address,
    token_id: TokenId,
    price: u64
}

DeFi Protocol Events:

move
struct SwapEvent has drop, store {
    user: address,
    coin_in_type: TypeInfo,
    coin_out_type: TypeInfo,
    amount_in: u64,
    amount_out: u64,
    fee: u64
}

Use Cases

Events enable powerful off-chain functionality:

  1. User Activity Feeds: Display transaction history and account activity in wallets and applications.

  2. Real-Time Notifications: Trigger webhooks or push notifications when specific events occur.

  3. Analytics Dashboards: Aggregate events to compute metrics like trading volume, user growth, or protocol usage.

  4. Compliance and Auditing: Maintain immutable audit trails of all contract interactions for regulatory requirements.

  5. State Reconstruction: Replay events to rebuild application state without querying all resources.

  6. Cross-Contract Coordination: Monitor events from other contracts to trigger conditional logic.

Best Practices

Structured Event Design: Design events with all information needed by consumers. Include addresses, amounts, timestamps, and type information.

Consistent Naming: Use descriptive event names with consistent patterns: TransferEvent, MintEvent, BurnEvent.

Emit After Success: Only emit events after operations succeed to avoid confusing off-chain systems with failed attempt notifications.

Version Events: When upgrading contracts, consider versioning events or adding optional fields to maintain backward compatibility with indexers.

Gas Efficiency: Events are relatively cheap but not free. Emit only essential notifications.

Type Information: Include type parameters or TypeInfo when events involve generic types to enable proper deserialization.

Event Querying

Access events via multiple endpoints:

REST API by Creation Number:

Text
GET /v1/accounts/{address}/events/{creation_number}

REST API by Handle:

Text
GET /v1/accounts/{address}/events/{event_handle}/{field_name}

GraphQL Indexer (when available): Provides complex filtering, aggregation, and joins across multiple event types.

Common Patterns

Event Handles (Legacy Pattern):

move
struct EventStore has key {
    transfer_events: EventHandle<TransferEvent>
}

Modern Event Emission (Recommended):

move
// No event handle needed
public entry fun transfer(from: &signer, to: address, amount: u64) {
    // Logic
    event::emit(TransferEvent { from: signer::address_of(from), to, amount });
}

The modern approach is simpler and more efficient.

  • Event Handles: Legacy mechanism for organizing events (still supported but not recommended for new code)
  • GraphQL Indexer: Query engine for complex event analysis
  • REST Event Endpoints: Direct event access via REST API
  • Transaction Metadata: Events are stored with transaction results