Skip to main content

Overview

This guide will walk you through creating a simple application that creates a wallet for a user and allows them to check their balance and send tokens. You’ll learn the core concepts of the Crossmint SDK.
Time to complete: ~5 minutesWhat you’ll build: A wallet creation app with balance checking and token transfers

Prerequisites

1

Node.js & Package Manager

Ensure you have Node.js 16+ and npm, yarn, or pnpm installed.
node --version  # Should be v16 or higher
2

Crossmint API Key

Get your API key from the Crossmint Console.Make sure your API key has these scopes enabled:
  • Wallet API (all permissions)
  • Users (all permissions)

Choose Your Path

React Application

Build with React hooks and providers

Vanilla TypeScript

Use the core SDK without a framework

React Quickstart

Step 1: Install Dependencies

npm install @crossmint/client-sdk-react-ui

Step 2: Set Up Environment Variables

Create a .env.local file in your project root:
.env.local
NEXT_PUBLIC_CROSSMINT_API_KEY=your_client_api_key_here
Use a client-side API key (starts with client_ or pk_). Never expose server-side keys in the browser.

Step 3: Configure Providers

Wrap your application with Crossmint providers:
app/layout.tsx
"use client";

import {
  CrossmintProvider,
  CrossmintAuthProvider,
  CrossmintWalletProvider,
} from "@crossmint/client-sdk-react-ui";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <CrossmintProvider apiKey={process.env.NEXT_PUBLIC_CROSSMINT_API_KEY!}>
          <CrossmintAuthProvider authModalTitle="Sign in to MyApp">
            <CrossmintWalletProvider
              createOnLogin={{
                chain: "polygon-amoy",
                signer: { type: "email" },
              }}
            >
              {children}
            </CrossmintWalletProvider>
          </CrossmintAuthProvider>
        </CrossmintProvider>
      </body>
    </html>
  );
}
What’s happening here?
  • CrossmintProvider - Initializes the SDK with your API key
  • CrossmintAuthProvider - Handles user authentication
  • CrossmintWalletProvider - Automatically creates a wallet when users log in

Step 4: Create Your Wallet Component

Build a component that displays wallet information and allows token transfers:
components/WalletDemo.tsx
"use client";

import { useAuth, useWallet } from "@crossmint/client-sdk-react-ui";
import { useState, useEffect } from "react";

export default function WalletDemo() {
  const { login, logout, user, status } = useAuth();
  const { wallet, status: walletStatus } = useWallet();
  const [balance, setBalance] = useState<string | null>(null);
  const [recipient, setRecipient] = useState("");
  const [amount, setAmount] = useState("");

  // Load wallet balance
  useEffect(() => {
    async function loadBalance() {
      if (wallet) {
        const balances = await wallet.balances();
        setBalance(balances.usdc.amount);
      }
    }
    loadBalance();
  }, [wallet]);

  // Handle token transfer
  async function sendTokens() {
    if (!wallet || !recipient || !amount) return;
    
    try {
      const transaction = await wallet.send(recipient, "usdc", amount);
      alert(`Transaction successful! View: ${transaction.explorerLink}`);
    } catch (error) {
      alert(`Transaction failed: ${error}`);
    }
  }

  // Show login button if not authenticated
  if (status === "logged-out") {
    return (
      <div style={{ padding: "2rem", textAlign: "center" }}>
        <h1>Welcome to Crossmint Wallet Demo</h1>
        <p>Sign in to create your wallet</p>
        <button onClick={login} style={buttonStyle}>
          Sign In
        </button>
      </div>
    );
  }

  // Show loading state
  if (walletStatus === "loading") {
    return <div style={{ padding: "2rem" }}>Loading wallet...</div>;
  }

  // Show wallet interface
  return (
    <div style={{ padding: "2rem", maxWidth: "600px", margin: "0 auto" }}>
      <h1>Your Wallet</h1>
      
      <div style={cardStyle}>
        <p><strong>Email:</strong> {user?.email}</p>
        <p><strong>Address:</strong> {wallet?.address}</p>
        <p><strong>USDC Balance:</strong> {balance || "Loading..."}</p>
      </div>

      <div style={cardStyle}>
        <h2>Send USDC</h2>
        <input
          type="text"
          placeholder="Recipient address"
          value={recipient}
          onChange={(e) => setRecipient(e.target.value)}
          style={inputStyle}
        />
        <input
          type="text"
          placeholder="Amount"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          style={inputStyle}
        />
        <button onClick={sendTokens} style={buttonStyle}>
          Send Tokens
        </button>
      </div>

      <button onClick={logout} style={{ ...buttonStyle, background: "#666" }}>
        Logout
      </button>
    </div>
  );
}

// Simple styles
const buttonStyle = {
  padding: "0.75rem 1.5rem",
  background: "#6366f1",
  color: "white",
  border: "none",
  borderRadius: "8px",
  cursor: "pointer",
  fontSize: "1rem",
  marginTop: "1rem",
};

const inputStyle = {
  width: "100%",
  padding: "0.75rem",
  marginBottom: "1rem",
  borderRadius: "8px",
  border: "1px solid #ddd",
  fontSize: "1rem",
};

const cardStyle = {
  background: "#f9fafb",
  padding: "1.5rem",
  borderRadius: "12px",
  marginBottom: "1rem",
};

Step 5: Run Your Application

npm run dev
Open http://localhost:3000 and test your wallet!
Success! You now have a working wallet application with:
  • User authentication via email OTP
  • Automatic wallet creation
  • Balance checking
  • Token transfers

Vanilla TypeScript Quickstart

For non-React applications or programmatic wallet management.

Step 1: Install Dependencies

npm install @crossmint/wallets-sdk

Step 2: Set Up Environment Variables

Create a .env file:
.env
CROSSMINT_API_KEY=your_api_key_here
You can use either a client-side or server-side API key. For server-side, you have access to additional features.

Step 3: Create a Wallet

Here’s a complete example that creates a wallet and performs operations:
index.ts
import { CrossmintWallets, createCrossmint } from "@crossmint/wallets-sdk";

// Initialize Crossmint
const crossmint = createCrossmint({
  apiKey: process.env.CROSSMINT_API_KEY || "",
  experimental_customAuth: {
    jwt: "<your-user-jwt>", // Required for client-side calls
  },
});

const crossmintWallets = CrossmintWallets.from(crossmint);

// Create or get existing wallet
const wallet = await crossmintWallets.getOrCreateWallet({
  chain: "polygon-amoy",
  signer: {
    type: "email",
    email: "[email protected]",
    onAuthRequired: async (needsAuth, sendEmailWithOtp, verifyOtp, reject) => {
      if (needsAuth) {
        // Send OTP email
        await sendEmailWithOtp();
        
        // In a real application, prompt the user for the OTP
        // For this example, we'll assume you have the OTP
        const otp = "123456"; // Get this from user input
        
        // Verify the OTP
        await verifyOtp(otp);
      }
    },
  },
});

console.log("Wallet created!");
console.log("Address:", wallet.address);

Step 4: Check Balances

Retrieve wallet balances:
const balances = await wallet.balances();

console.log("Native Token:", balances.nativeToken.amount);
console.log("USDC:", balances.usdc.amount);

Step 5: Send Tokens

Transfer tokens to another address:
const transaction = await wallet.send(
  "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", // recipient
  "usdc",  // token
  "10.5"   // amount
);

console.log("Transaction successful!");
console.log("Explorer link:", transaction.explorerLink);

Step 6: Advanced Operations

For chain-specific operations:
import { SolanaWallet, EVMWallet } from "@crossmint/wallets-sdk";

// Solana-specific operations
const solanaWallet = SolanaWallet.from(wallet);
const solTx = await solanaWallet.sendTransaction({
  transaction: "<serialized-transaction>",
});

// EVM-specific operations
const evmWallet = EVMWallet.from(wallet);
const evmTx = await evmWallet.sendTransaction({
  transaction: "<serialized-transaction>",
});

console.log("Solana TX:", solTx.explorerLink);
console.log("EVM TX:", evmTx.explorerLink);

Step 7: Run Your Script

node --loader ts-node/esm index.ts
# or with tsx
tsx index.ts
Success! You can now create and manage wallets programmatically.

Understanding Key Concepts

Signers determine how wallet transactions are authorized. Crossmint supports:
  • Email - User authenticates via email OTP
  • Passkey - WebAuthn/biometric authentication
  • API Key - Programmatic access (server-side only)
  • External Wallet - Use existing wallet for signing
// Email signer
{ type: "email", email: "[email protected]" }

// API key signer (server-side)
{ type: "api-key" }
The same code works across all supported chains:Solana:
  • solana (mainnet)
  • solana-devnet
EVM Chains:
  • polygon, polygon-amoy
  • base, base-sepolia
  • ethereum, sepolia
  • arbitrum, arbitrum-sepolia
  • And 15+ more…
// Just change the chain parameter
await crossmintWallets.getOrCreateWallet({
  chain: "base", // or "solana", "polygon", etc.
  signer: { type: "email", email: "[email protected]" },
});
For email-based authentication:
  1. User provides email address
  2. Crossmint sends OTP code
  3. User enters OTP
  4. Wallet is created/accessed
onAuthRequired: async (needsAuth, sendEmailWithOtp, verifyOtp, reject) => {
  if (needsAuth) {
    await sendEmailWithOtp();
    // Show OTP input to user
    const otp = await getUserOtp(); // Your UI logic
    await verifyOtp(otp);
  }
}
When using React SDK, this is handled automatically by the provider!
Track all wallet transactions:
const activity = await wallet.experimental_activity();

activity.events.forEach(event => {
  console.log(event.type);      // "send", "receive", etc.
  console.log(event.timestamp);
  console.log(event.amount);
});
Allow other addresses to sign transactions on behalf of the wallet:
// Add a delegated signer
await wallet.addDelegatedSigner({
  signer: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
});

// List all delegated signers
const signers = await wallet.delegatedSigners();
console.log(signers);

Next Steps

Wallets SDK Deep Dive

Explore advanced wallet features and multi-chain support

Authentication Guide

Learn about authentication options and security

React Components

Discover all available React hooks and components

API Reference

Browse the complete API documentation

Common Patterns

// Create wallets on different chains
const polygonWallet = await crossmintWallets.getOrCreateWallet({
  chain: "polygon",
  signer: { type: "email", email: user.email },
});

const solanaWallet = await crossmintWallets.getOrCreateWallet({
  chain: "solana",
  signer: { type: "email", email: user.email },
});

// Same user, different chains
console.log("Polygon:", polygonWallet.address);
console.log("Solana:", solanaWallet.address);

Troubleshooting

If the OTP email isn’t arriving:
  1. Check spam/junk folder
  2. Verify email address is correct
  3. Ensure API key has “Users” scope enabled
  4. Check Crossmint console for rate limits
Common reasons for transaction failures:
  1. Insufficient balance - Check wallet has enough tokens + gas
  2. Invalid address - Verify recipient address format
  3. Wrong chain - Ensure recipient address matches chain
  4. Network issues - Try again after a moment
If wallet creation fails:
  1. Verify API key is valid and has correct scopes
  2. Check that chain name is spelled correctly
  3. Ensure JWT is provided for client-side calls
  4. Review error message for specific details

Need Help?

Join our Discord community or check the full documentation for more assistance.

Build docs developers (and LLMs) love