Skip to main content

System Architecture

WizPay is a monorepo containing a NestJS backend, a Next.js frontend, and shared protocol configuration. The testnet bridge is a direct External Wallet CCTP V2 lifecycle: the browser wallet signs approval, burn, and destination receiveMessage, while the backend validates receipts, retrieves and validates attestations, and restores the persisted lifecycle without a generic retry workflow.

Component Topology

Component Responsibilities

Frontend (Next.js)

  • Composes payment payloads from user input
  • Calls backend HTTP endpoints to create tasks
  • Polls GET /tasks/:id for progress and renders status
  • Manages wallet sessions (W3S userToken / passkey)
  • In PASSKEY mode: signs and broadcasts transactions client-side
  • For External Wallet bridge routes: signs exact USDC approval, CCTP V2 burn, and destination receiveMessage transactions in the browser
App Wallet mode does not expose Bridge. The External Wallet bridge uses the canonical testnet registry and durable /bridge/intents lifecycle; the backend never signs or submits bridge transactions.

Mobile Shell

The frontend adapts to mobile viewports through a dedicated shell layer:
  • Bottom Navigation — 4-tab fixed bar (Home, Swap, Liquidity, Profile). Visible on mobile; hidden at the md breakpoint and above. Desktop navigation is rendered separately in the sidebar.
  • Mobile Faucet Card — A compact card on the home screen (mobile-only) that displays the user’s wallet address with a one-tap copy action and a direct link to the testnet faucet. Hidden on desktop.
  • Profile / Account Center — A dedicated /profile route (ProfileHubPage) containing wallet identity, wallet address display, linked social metadata, and the PWA install prompt on eligible devices.

Progressive Web App (PWA)

WizPay ships a complete PWA surface for add-to-home-screen installability on mobile and desktop: The install prompt shown in the Profile hub gates visibility on a real installability signal (nativePromptAvailable || manualInstallAvailable) in addition to the mobile/not-installed/not-dismissed checks, preventing the prompt from appearing on platforms where installation is not possible.

Circle Mobile Session Recovery

On mobile devices, Circle W3S SDK sessions can silently expire when the browser is backgrounded. The frontend implements a provider-owned recovery layer:
  • useMobileRecovery — Listens to visibilitychange, focus, pageshow, and online browser events. On each trigger (throttled) it calls rearmSdkForSession to re-hydrate the SDK with the current auth token.
  • rearmSdkForSession — Sets the current user token on the Circle SDK instance without a full re-initialisation.
  • ensureSessionReady — Exposed on CircleWalletContextValue. Called by useTransactionExecutor and the bridge screen before every Circle-mode operation. If the session is expired, arms the SDK and refreshes wallets before proceeding.
  • withRecoveredSession — Wrapper inside useChallengeActions. Catches recoverable Circle session errors (code 155706 and related invalid-device codes), calls ensureSessionReady, and retries the failed operation exactly once.

Orchestrator

  • OrchestratorService.handleTask() — HTTP entry point. Creates task, sets status to assigned, enqueues to BullMQ.
  • OrchestratorService.executeTask() — Worker entry point. Idempotency guard → status to in_progress → route to agent → finalize.
  • Legacy type: bridge task submissions fail closed before task creation.

Task Module

Execution Layer

  • ExecutionRouterService — Reads walletMode from task payload. Routes to AgentRouterService (W3S) or PasskeyEngineService (PASSKEY).
  • PasskeyEngineService — Handles its remaining non-bridge task types. Bridge is not routed through an execution engine.

Agents

Each agent implements the TaskAgent interface:

Adapters

Queue

Trust Boundaries

  • All user input crosses the trust boundary at TaskController and is validated before reaching the orchestrator.
  • The bridge lifecycle cannot access Circle entity secrets, backend signing keys, or treasury wallets.
  • In PASSKEY mode, the backend has no signing authority over the user’s wallet. The trust model shifts — the backend produces unsigned intents, the client signs.
  • In external-wallet bridge mode, the backend also has no signing authority. It validates the submitted bridge metadata, records the audit trail, and leaves the burn/mint execution to the connected browser wallet.

Infrastructure