⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip Alchemy's expensive compute units.
Switch Today →
Skip to main content

events

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:

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:

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

NFT Marketplace Events:

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:

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:

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

REST API by Handle:

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):

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

Modern Event Emission (Recommended):

// 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