DEV Community

999DEX
999DEX

Posted on • Originally published at 999dex.com

How We Track User Onboarding on a Decentralised Exchange (Without Cookies or Third Parties)

The DEX Analytics Problem

Building analytics for a decentralised exchange is harder than it sounds. You have no accounts, no logins, no cookies. Users are pseudonymous wallet addresses. Traditional analytics tools like Google Analytics track page visits but know nothing about blockchain activity.

We needed to answer: How many wallets are connecting? What are they doing? Which pages drive trading?

Here is how we built it.

The Architecture

1. Frontend: useAnalytics Hook

We wrote a lightweight React hook that fires events to our backend:

export function useAnalytics() {
  const { address } = useAccount(); // wagmi

  const track = useCallback((event, metadata = {}) => {
    fetch("/api/analytics/track", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        event,
        wallet: address || null,
        page: window.location.pathname,
        metadata,
      }),
      keepalive: true, // fire-and-forget, never blocks UI
    }).catch(() => {});
  }, [address]);

  return { track };
}
Enter fullscreen mode Exit fullscreen mode

Key design decision: fire-and-forget. The keepalive: true flag means the request completes even if the user navigates away. We never await it — the UI never waits for analytics.

2. Automatic Tracking in App.jsx

We added two invisible components at the router root:

function PageTracker() {
  const { pathname } = useLocation();
  const { track } = useAnalytics();
  useEffect(() => {
    track("page_view", { page: pathname });
  }, [pathname]);
  return null;
}

function WalletTracker() {
  const { address, isConnected } = useAccount();
  const { track } = useAnalytics();
  const prevAddress = useRef(null);
  useEffect(() => {
    if (isConnected && address && address !== prevAddress.current) {
      prevAddress.current = address;
      track("wallet_connect", { wallet: address });
    }
  }, [isConnected, address]);
  return null;
}
Enter fullscreen mode Exit fullscreen mode

Every page visit and every wallet connection is tracked automatically — without touching any individual component.

3. Backend: Redis Storage

The /api/analytics/track endpoint writes to Redis:

// Unique wallets (set — no duplicates)
await cache.sadd("analytics:wallets:all", addr);
await cache.sadd(`analytics:wallets:daily:${day}`, addr);

// Event counters
await cache.incr(`analytics:total:${event}`);
await cache.incr(`analytics:daily:${day}:${event}`);

// Page view counter
await cache.incr(`analytics:pages:${page}`);
Enter fullscreen mode Exit fullscreen mode

No SQL. No third-party service. No data leaving our server. Redis SADD naturally deduplicates wallet addresses across page reloads.

4. The Dashboard

/analytics on 999dex.com now shows:

  • Total unique wallets ever connected
  • Daily Active Users (DAU)
  • New wallets today
  • 7-day DAU bar chart
  • All-time action counts per feature
  • Top pages by visit count

What We Track

Event Trigger
page_view Any page navigation
wallet_connect First wallet connection per session
trade_buy Confirmed bonding curve buy
trade_sell Confirmed bonding curve sell
token_create New token deployed
stake $999 staked
studio_deploy Contract deployed via Studio

Privacy

Wallet addresses are public on-chain — they are not personally identifiable by themselves. We store no IP addresses, no email, no device fingerprint. The only identifier is the wallet address the user chose to connect.

The Result

Within 24 hours of launch, we could see exactly which pages users were hitting, which wallets were new, and where in the funnel users were dropping off. This kind of insight is normally only available to centralised platforms.

Decentralised does not have to mean blind.

Platform: 999dex.com · BlockDAG Mainnet (Chain 1404)

Top comments (0)