Docs Navigation

Embedded Wallet

Embedded Wallet: Analytics

Forward embedded wallet product telemetry from proxy.analytics to GA4 or your analytics stack — subscribe in the Host, filter PII, and configure GA4 forwarding.

Overview

The 1Shot Wallet Branding Layer (wallet.1shotapi.com) publishes product analytics events to your Host app over Postmate (ows:analytics). Your Host receives the full payload through proxy.analytics.on() after you create an OWSProxy — the same integration surface any customer uses.

At the wire layer this is vendor-agnostic: you choose whether to forward events to Google Analytics 4, Segment, PostHog, a first-party API, or all of the above. The sections below document a complete GA4 integration you can copy, including the PII-safe parameter policy used on www.1shotapi.com.

Subscribe in your Host app

Subscribe once after OWSProxy.create() succeeds. You do not need a separate handler per event name — every product event flows through the same listener. Call the returned unsubscribe function (or rely on proxy.destroy()) before tearing down the embed.

Minimal subscription

import { OWSProxy } from "@1shotapi/ows-provider";

const container = document.getElementById("wallet-container")!;
const proxy = await OWSProxy.create(container, "https://wallet.1shotapi.com/");

const unsubscribe = proxy.analytics.on((event) => {
  console.info(event.name, event);
  // Forward to your analytics vendor here
});

// On teardown:
unsubscribe();
proxy.destroy();

Optional helper (mirrors 1Shot marketing site)

function subscribeWalletAnalytics(
  proxy: OWSProxy,
  onEvent: (event: IOWSAnalyticsEvent) => void,
): () => void {
  return proxy.analytics.on(onEvent);
}

// After create:
const unsubscribe = subscribeWalletAnalytics(proxy, (event) => {
  trackWalletAnalyticsEvent(event, "checkout"); // your surface label
});
Note

You can also narrow with proxy.analytics.on("PersonalSign", listener) when you only care about one name, but a single catch-all listener is enough for GA4.

Event catalog (27 names)

Each event includes base fields: eventId, timestamp, hostDomain, and name. The wallet attaches additional fields depending on the action (for example durationMs, chainId, errorCode). Narrow on event.name in your forwarder.

  • AccountAccountCreated, AccountCreateFailed, AccountCreateCancelled (accountAddress, errorCode on failures)
  • EIP-191 (personal_sign)PersonalSign, PersonalSignFailed, PersonalSignCancelled (messageLength, durationMs, errorCode)
  • EIP-712 (typed data)TypedSign, TypedSignFailed, TypedSignCancelled (primaryType, durationMs, errorCode)
  • SendTransactionSubmitted, TransactionSubmitFailed, TransactionSubmitCancelled (chainId, to, txHash, methodId, durationMs, errorCode)
  • Credentials (OID4)CredentialIssued, CredentialIssueFailed, CredentialIssueCancelled, CredentialPresented, CredentialPresentFailed, CredentialPresentCancelled (issuerOrigin, verifierOrigin, durationMs, errorCode)
  • Delegations (EIP-7715)DelegationCreated, DelegationCreateFailed, DelegationCreateCancelled, DelegationCancelled, DelegationCancelFailed, DelegationCancelAborted (chainId, txHash on revoke success, durationMs, errorCode)

PII-safe forwarding

Wallet events can include on-chain identifiers. Third-party analytics (including GA4) should not receive values you could tie to a specific user or wallet.

Forward: host context you define (for example wallet_surface = checkout), host_domain from event.hostDomain, timing (duration_ms), chain (chain_id), failure codes (error_code), message metadata (message_length, primary_type, method_id), and credential party origins (issuer_origin, verifier_origin).

Do not forward: accountAddress, to, txHash.

GA4 event naming and parameters

Map each wallet event.name to a GA4 custom event: wallet_{snake_case(name)}. Example: PersonalSignwallet_personal_sign, TransactionSubmitFailedwallet_transaction_submit_failed. All 27 names fit GA4's 40-character event name limit.

Attach parameters using snake_case. At minimum send event_category: "wallet", wallet_event_name (original name), host_domain, and a host-defined wallet_surface (or equivalent) so you can segment embed locations in reports.

GA4 forwarder (reference implementation)

import type { IOWSAnalyticsEvent } from "@1shotapi/ows-types";

declare global {
  interface Window {
    gtag?: (...args: unknown[]) => void;
  }
}

const WALLET_FIELD_TO_GA_PARAM: Record<string, string> = {
  durationMs: "duration_ms",
  chainId: "chain_id",
  errorCode: "error_code",
  messageLength: "message_length",
  primaryType: "primary_type",
  methodId: "method_id",
  issuerOrigin: "issuer_origin",
  verifierOrigin: "verifier_origin",
};

function walletEventToGaEventName(name: string): string {
  return `wallet_${name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase()}`;
}

function trackWalletAnalyticsEvent(
  event: IOWSAnalyticsEvent,
  surface: string,
): void {
  if (typeof window.gtag !== "function") return;

  const params: Record<string, string | number | boolean> = {
    event_category: "wallet",
    wallet_surface: surface,
    wallet_event_name: event.name,
    host_domain: String(event.hostDomain),
  };

  for (const [key, gaKey] of Object.entries(WALLET_FIELD_TO_GA_PARAM)) {
    const value = event[key];
    if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
      params[gaKey] = value;
    }
  }

  window.gtag("event", walletEventToGaEventName(event.name), params);
}

The live marketing site implementation lives in app/lib/wallet-analytics.ts and uses wallet_surface values homepage, wallet_page, and playground.

Load GA4 in your app

Environment variable

NEXT_PUBLIC_GA4_MEASUREMENT_ID=G-XXXXXXXXXX

Load the gtag snippet once site-wide with your measurement ID (G-XXXXXXXXXX). Guard trackWalletAnalyticsEvent with typeof window.gtag === "function" so local dev without GA does not throw.

On the 1Shot marketing site, GA4 is loaded from app/components/Analytics.tsx via NEXT_PUBLIC_GA4_MEASUREMENT_ID (or an inline measurement ID in app/layout.tsx).

Configure Google Analytics 4

GA4 collects custom event names automatically — you do not pre-register each wallet_* event. Configure the portal so reports break out the parameters you care about.

Note

Realtime updates in seconds; the main Events report can lag 24–48 hours. Custom dimensions apply to hits collected after registration.

  1. Verify hits — Open GA4 Admin → DebugView. Install the Google Analytics Debugger Chrome extension (or enable debug_mode temporarily). Trigger wallet actions in your app and confirm events such as wallet_personal_sign and wallet_account_created appear with wallet_surface and other params.
  2. Register custom dimensionsAdmin → Data display → Custom definitions → Create custom dimension (scope: Event). Register at least: wallet_surface, wallet_event_name, host_domain, chain_id, error_code, duration_ms. Parameter names must match what your forwarder sends.
  3. Mark key events (optional)Admin → Data display → Events. After events appear (often 24–48 hours in this list; DebugView is immediate), toggle Mark as key event for outcomes you track as conversions, for example wallet_account_created or wallet_transaction_submitted.
  4. Build Explore reports — Use Explore → Free form or Funnel exploration. Filter event names to wallet_* or break down by the wallet_surface custom dimension to compare embed locations.

Test before production

Use the wallet playground Analytics mode to inspect the live proxy.analytics stream (filter by name) while exercising connect, sign, send, credential, and delegation flows. The marketing playground also forwards the same events to GA4 using the reference forwarder above.