ArchitectureEvent System Architecture

Event System Architecture

The Wafra backend implements a robust, UI-focused event system for tracking user and business flows. This system is designed for real-time status updates, user feedback, and auditability of key actions across the platform.

Purpose

  • User Experience: Provide real-time, contextual feedback to users (e.g., account creation, device management, transactions)
  • Audit Trail: Maintain a clear, queryable record of user actions and system flows
  • UI Simplicity: Decouple UI status from blockchain transactions and business logic

Event Model

Events are stored in the Event table in the database. Each event is:

  • Immutable (except for status/title/description updates)
  • Identified by a unique event ID (UUID)
  • Linked to a user
  • Typed (see below)

Event Types

  • account_creation — Account onboarding and wallet setup
  • device_added — New device/passkey registered or approved
  • device_removed — Device/passkey removed
  • top_up — User initiates a top-up
  • deposit — User initiates a deposit
  • send — User sends funds
  • withdraw — User withdraws funds

Event Status

  • pending — Awaiting completion
  • in_progress — Actively being processed
  • completed — Successfully finished
  • failed — Ended with an error

Event API Usage

All event updates must use the event ID. There is no support for updating by userId+type.

Creating an Event

import { createEvent } from "@/services/tasks/utils/event.util";
 
const eventId = await createEvent({
  userId: user.id,
  type: "account_creation",
  title: "Creating your secure wallet...",
  status: "in_progress",
});

Updating an Event

import { updateEvent } from "@/services/tasks/utils/event.util";
 
await updateEvent(eventId, {
  status: "completed",
  title: "Account created successfully",
  description: "Your secure wallet is ready to use",
});

Completing or Failing an Event

import { completeEvent, failEvent } from "@/services/tasks/utils/event.util";
 
await completeEvent(eventId, "Done!", "Everything worked");
await failEvent(eventId, "Failed", "Something went wrong");

Real-Time Updates

All event changes are pushed to the user in real-time via socket notifications. The event payload includes:

  • eventId
  • type
  • status
  • title
  • description
  • data (optional)
  • timestamp

Best Practices

  • Always store the event ID when creating an event for later updates.
  • Never update by userId+type — this is not supported and will not work.
  • Use events for UI/flow status, not for blockchain or financial transaction records (see the Transaction model for those).

Example: Account Creation Flow

  1. Start:
    const eventId = await createEvent({
      userId,
      type: "account_creation",
      title: "Creating account...",
      status: "in_progress",
    });
  2. Update:
    await updateEvent(eventId, {
      title: "Wallet created",
      status: "in_progress",
      data: { safeAddress },
    });
  3. Complete:
    await completeEvent(eventId, "Account created", "Your wallet is ready");

Migration Note

Legacy support for updating events by userId+type has been removed. All event updates must use the event ID.


For more details, see the event utility code.