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 setupdevice_added— New device/passkey registered or approveddevice_removed— Device/passkey removedtop_up— User initiates a top-updeposit— User initiates a depositsend— User sends fundswithdraw— User withdraws funds
Event Status
pending— Awaiting completionin_progress— Actively being processedcompleted— Successfully finishedfailed— 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:
eventIdtypestatustitledescriptiondata(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
Transactionmodel for those).
Example: Account Creation Flow
- Start:
const eventId = await createEvent({ userId, type: "account_creation", title: "Creating account...", status: "in_progress", }); - Update:
await updateEvent(eventId, { title: "Wallet created", status: "in_progress", data: { safeAddress }, }); - 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.