Skip to main content

Overview

This guide covers implementation patterns for common Web3 analytics scenarios. Use these patterns to ensure comprehensive, accurate tracking.

Implementation Workflow

1

Plan Your Events

Define what you want to measure before writing code
2

Install SDK

Add Cryptique to your application
3

Configure Auto-Events

Enable/disable automatic tracking
4

Add Custom Events

Track business-specific actions
5

Set Up Identification

Link users across sessions
6

Integrate Wallets

Connect wallet events to user profiles
7

Verify & Iterate

Test tracking and refine

Common Patterns

Authentication Flow

Track the complete auth journey:
// Signup started
Cryptique.track('signup_started', {
  signup_method: 'email',  // or 'google', 'wallet'
  referral_source: getReferralSource()
});

// Signup completed
Cryptique.track('signup_completed', {
  signup_method: 'email',
  time_to_complete_seconds: 45
});

// Identify the user
Cryptique.identify(user.id);

// Set user properties
Cryptique.people.set({
  email: user.email,
  name: user.name,
  signup_date: new Date().toISOString(),
  signup_method: 'email'
});

// Set persistent properties
Cryptique.people.set_once({
  first_seen: new Date().toISOString(),
  original_referrer: document.referrer
});

Wallet Connection Flow

Track wallet interactions:
// Wallet connection initiated
Cryptique.track('wallet_connect_initiated', {
  wallet_type: 'metamask',
  trigger_location: 'navbar'  // or 'modal', 'onboarding'
});

// Wallet connected successfully
async function onWalletConnected(address, provider) {
  // Track with SDK
  Cryptique.walletAddress(address);
  Cryptique.track('wallet_connected', {
    wallet_type: getWalletType(provider),
    chain_id: await provider.getChainId()
  });
  
  // Optional: set user properties
  Cryptique.people.set({
    primary_wallet: address,
    last_wallet_connect: new Date().toISOString()
  });
  
  Cryptique.people.increment({
    wallet_connect_count: 1
  });
}

// Wallet connection failed
function onWalletError(error) {
  Cryptique.track('wallet_connect_failed', {
    error_type: error.code,
    error_message: error.message,
    wallet_type: 'metamask'
  });
}

// Wallet disconnected
function onWalletDisconnected(address) {
  Cryptique.track('wallet_disconnected', { address });
}

// Chain changed
function onChainChanged(chainId, address) {
  Cryptique.track('chain_changed', {
    new_chain_id: chainId,
    wallet_address: address
  });
}

DeFi Swap Flow

Track the complete swap journey:
// Token selected
Cryptique.track('token_selected', {
  token_symbol: 'ETH',
  token_address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
  selection_type: 'input',  // or 'output'
  search_query: searchTerm || null
});

// Swap quoted
Cryptique.track('swap_quoted', {
  input_token: 'ETH',
  output_token: 'USDC',
  input_amount: 1.5,
  output_amount: 1485.50,
  exchange_rate: 990.33,
  slippage_setting: 0.5,
  route_provider: 'uniswap'
});

// Swap initiated (user clicked swap)
Cryptique.track('swap_initiated', {
  input_token: 'ETH',
  output_token: 'USDC',
  input_amount: 1.5,
  output_amount_expected: 1485.50,
  slippage_setting: 0.5
});

// Transaction approval requested
Cryptique.track('transaction_approval_requested', {
  token: 'USDC',
  spender: '0x...router',
  amount: 'unlimited'  // or specific amount
});

// Swap completed (after on-chain confirmation)
Cryptique.track('swap_completed', {
  input_token: 'ETH',
  output_token: 'USDC',
  input_amount: 1.5,
  output_amount_actual: 1483.20,
  slippage_actual: 0.15,
  transaction_hash: txHash,
  gas_used: gasUsed,
  gas_price_gwei: gasPriceGwei
});

// Swap failed
Cryptique.track('swap_failed', {
  input_token: 'ETH',
  output_token: 'USDC',
  input_amount: 1.5,
  error_type: 'slippage_exceeded',  // or 'user_rejected', 'insufficient_gas'
  error_message: error.message
});

NFT Mint Flow

// Collection viewed
Cryptique.track('collection_viewed', {
  collection_name: 'Cool Cats',
  collection_address: '0x...',
  items_available: 500,
  price_eth: 0.05
});

// Mint initiated
Cryptique.track('mint_initiated', {
  collection_name: 'Cool Cats',
  quantity: 2,
  total_price_eth: 0.10,
  mint_type: 'public'  // or 'whitelist', 'allowlist'
});

// Mint completed
Cryptique.track('mint_completed', {
  collection_name: 'Cool Cats',
  quantity: 2,
  token_ids: [1234, 1235],
  total_price_eth: 0.10,
  transaction_hash: txHash
});

Onboarding Flow

Track multi-step onboarding:
const ONBOARDING_STEPS = [
  'welcome',
  'connect_wallet',
  'verify_email',
  'set_preferences',
  'first_action'
];

function trackOnboardingStep(step, additionalProps = {}) {
  const stepIndex = ONBOARDING_STEPS.indexOf(step);
  
  Cryptique.track('onboarding_step_completed', {
    step_name: step,
    step_number: stepIndex + 1,
    total_steps: ONBOARDING_STEPS.length,
    ...additionalProps
  });
  
  // Update user property
  Cryptique.people.set({
    onboarding_step: step,
    onboarding_progress: Math.round((stepIndex + 1) / ONBOARDING_STEPS.length * 100)
  });
}

// On completion
function completeOnboarding() {
  Cryptique.track('onboarding_completed', {
    total_time_seconds: timeSinceStart,
    steps_completed: completedSteps.length,
    skipped_steps: skippedSteps
  });
  
  Cryptique.people.set({
    onboarding_completed: true,
    onboarding_completed_date: new Date().toISOString()
  });
}

Error Tracking

Track errors for debugging:
// Generic error tracking
function trackError(error, context) {
  Cryptique.track('error_occurred', {
    error_type: error.name,
    error_message: error.message,
    error_code: error.code,
    context: context,
    page: window.location.pathname
  });
}

// Transaction error
function trackTransactionError(error, txDetails) {
  Cryptique.track('transaction_error', {
    error_type: classifyError(error),
    error_message: error.message,
    transaction_type: txDetails.type,
    chain_id: txDetails.chainId,
    gas_estimate: txDetails.gasEstimate
  });
}

// Classify errors for better analysis
function classifyError(error) {
  if (error.code === 4001) return 'user_rejected';
  if (error.code === -32603) return 'internal_error';
  if (error.message.includes('insufficient funds')) return 'insufficient_funds';
  if (error.message.includes('gas')) return 'gas_error';
  return 'unknown';
}

Framework-Specific Patterns

React with Wagmi

import { useAccount, useConnect, useDisconnect } from 'wagmi';
import { useEffect } from 'react';

function WalletTracker() {
  const { address, isConnected, chain } = useAccount();
  const { connect, connectors } = useConnect();
  const { disconnect } = useDisconnect();
  
  // Track connection state changes
  useEffect(() => {
    if (isConnected && address) {
      Cryptique.walletAddress(address);
      Cryptique.track('wallet_connected', {
        chain_id: chain?.id,
        wallet_type: connectors[0]?.name || 'unknown'
      });
    }
  }, [isConnected, address, chain]);
  
  // Track disconnection
  const handleDisconnect = () => {
    if (address) {
      Cryptique.track('wallet_disconnected', { address });
    }
    disconnect();
  };
  
  return (/* ... */);
}

Next.js App Router

// app/providers.jsx
'use client';

import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import Cryptique from 'cryptique-sdk';

export function AnalyticsProvider({ children }) {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  
  // Track page views on route change
  useEffect(() => {
    // Auto page_view should handle this, but for SPAs:
    Cryptique.track('page_view', {
      page_path: pathname,
      query_string: searchParams.toString()
    });
  }, [pathname, searchParams]);
  
  return children;
}

Vue 3 Composition API

// composables/useTracking.js
import { watch } from 'vue';
import { useRoute } from 'vue-router';
import Cryptique from 'cryptique-sdk';

export function useTracking() {
  const route = useRoute();
  
  // Track route changes
  watch(
    () => route.fullPath,
    (newPath) => {
      Cryptique.track('page_view', {
        page_path: newPath
      });
    }
  );
  
  const trackEvent = (name, properties) => {
    Cryptique.track(name, properties);
  };
  
  return { trackEvent };
}

Groups (companies, teams, workspaces)

Model B2B or multi-tenant context with Groups: assign the user, then set properties on the group profile.
Cryptique.identify(user.id);

await Cryptique.set_group('company', user.companyId);

const company = Cryptique.get_group('company', user.companyId);
await company.set({
  name: user.companyName,
  plan: user.companyPlan
});

Cryptique.track('report_exported', { format: 'csv' });
Use add_group / remove_group when a user can belong to multiple IDs under the same key. After identify(), the server may return group_memberships to hydrate assignments on a new device.

Testing Your Implementation

Debug Mode

Enable debug logging:
// CDN
script.setAttribute('debug', 'true');

// npm
await Cryptique.init({
  siteId: 'YOUR_SITE_ID',
  debug: true  // Logs all events to console
});

Verification Checklist

1

Check Live Events

Go to Dashboard → Live Events and perform actions
2

Verify User Profiles

Check that identify() creates/updates profiles
3

Test Wallet Linking

Connect wallet and verify it appears on profile
4

Check Properties

Verify all expected properties are present
5

Test Error Cases

Trigger errors and verify tracking

Common Issues

  • Check Site ID is correct
  • Verify domain is allowed in settings
  • Check for ad blocker interference
  • Look for console errors
  • Ensure identify() is called after SDK loads
  • Verify distinct_id is a string
  • Check that properties are valid JSON
  • Call Cryptique.walletAddress() with a valid address
  • Verify address format (0x…)
  • Confirm the SDK session initialized (Site ID, domain allowlist)
  • Check SDK isn’t initialized twice
  • Verify event handlers aren’t double-bound
  • Review SPA routing configuration

Next Steps

SDK Reference

Full method documentation

Build Reports

Analyze your data