Wallet Creation and Management
This guide demonstrates how to create and manage wallets using the Crossmint SDK, including balance checking, token transfers, and advanced operations.Demo Repository
Wallets Quickstart
Complete wallet integration example with UI components (Live Demo)
Installation
npm install @crossmint/client-sdk-react-ui
Setup
React Setup
Wrap your application with the required providers:"use client";
import {
CrossmintProvider,
CrossmintAuthProvider,
CrossmintWalletProvider
} from "@crossmint/client-sdk-react-ui";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<CrossmintProvider apiKey={process.env.NEXT_PUBLIC_CROSSMINT_API_KEY}>
<CrossmintAuthProvider
loginMethods={["google", "twitter", "email"]}
>
<CrossmintWalletProvider
createOnLogin={{
chain: "solana", // or "polygon", "base", "ethereum", etc.
signer: { type: "email" }
}}
>
{children}
</CrossmintWalletProvider>
</CrossmintAuthProvider>
</CrossmintProvider>
);
}
Wallets SDK Setup
import { CrossmintWallets, createCrossmint } from "@crossmint/wallets-sdk";
const crossmint = createCrossmint({
apiKey: process.env.CROSSMINT_API_KEY,
experimental_customAuth: {
jwt: "<your-jwt>", // Required for client-side calls
},
});
const crossmintWallets = CrossmintWallets.from(crossmint);
Creating Wallets
Basic Wallet Creation
import { useWallet } from "@crossmint/client-sdk-react-ui";
export default function CreateWallet() {
const { wallet, status, getOrCreateWallet } = useWallet();
// Wallet is automatically created when using createOnLogin in provider
// Or create manually:
const createSolanaWallet = async () => {
const wallet = await getOrCreateWallet({
chain: "solana",
signer: { type: "email" }
});
console.log("Wallet address:", wallet.address);
};
if (status === "loaded") {
return (
<div>
<p>Wallet Address: {wallet?.address}</p>
<p>Chain: {wallet?.chain}</p>
</div>
);
}
return <p>Loading wallet...</p>;
}
Multi-Chain Wallet Creation
Create wallets for different blockchains:import { useState } from "react";
import { useWallet } from "@crossmint/client-sdk-react-ui";
type ChainType = "solana" | "polygon" | "base" | "ethereum";
export default function MultiChainWallet() {
const { wallet, getOrCreateWallet } = useWallet();
const [selectedChain, setSelectedChain] = useState<ChainType>("solana");
const switchChain = async (chain: ChainType) => {
const newWallet = await getOrCreateWallet({
chain,
signer: { type: "email" }
});
setSelectedChain(chain);
};
return (
<div className="flex flex-col gap-4">
<div>
<p className="font-bold">Current Chain: {wallet?.chain}</p>
<p>Address: {wallet?.address}</p>
</div>
<div className="flex gap-2">
<button onClick={() => switchChain("solana")}>Solana</button>
<button onClick={() => switchChain("polygon")}>Polygon</button>
<button onClick={() => switchChain("base")}>Base</button>
<button onClick={() => switchChain("ethereum")}>Ethereum</button>
</div>
</div>
);
}
Crossmint supports 20+ EVM chains including Ethereum, Polygon, Base, Arbitrum, Optimism, and Solana. See the full list in the chains reference.
Checking Balances
Display Wallet Balances
import { useEffect, useState } from "react";
import { type Balances, useWallet } from "@crossmint/client-sdk-react-ui";
export function WalletBalance() {
const { wallet } = useWallet();
const [balances, setBalances] = useState<Balances | null>(null);
useEffect(() => {
async function fetchBalances() {
if (!wallet) return;
try {
const balances = await wallet.balances(["usdxm"]);
setBalances(balances);
} catch (error) {
console.error("Error fetching balances:", error);
}
}
fetchBalances();
}, [wallet]);
if (!balances) return <p>Loading balances...</p>;
const formatBalance = (amount: string) => parseFloat(amount).toFixed(2);
return (
<div className="flex flex-col gap-2">
{/* Native token */}
<div className="flex justify-between items-center">
<p className="font-medium">
{wallet?.chain === "solana" ? "SOL" : "ETH"}
</p>
<p className="font-medium">
{formatBalance(balances.nativeToken.amount)}
</p>
</div>
{/* USDXM token */}
<div className="flex justify-between items-center">
<p className="font-medium">USDXM</p>
<p className="font-medium">
${formatBalance(
balances.tokens?.find(t =>
t.symbol?.toLowerCase() === "usdxm"
)?.amount ?? "0"
)}
</p>
</div>
</div>
);
}
Transferring Tokens
EVM Transfer
import { useState } from "react";
import { useWallet } from "@crossmint/client-sdk-react-ui";
import { isAddress } from "viem";
export function EVMTransferFunds() {
const { wallet } = useWallet();
const [token, setToken] = useState<"eth" | "usdxm">("eth");
const [recipient, setRecipient] = useState("");
const [amount, setAmount] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [txLink, setTxLink] = useState<string | null>(null);
async function handleTransfer() {
if (!wallet || !recipient || !amount) {
alert("Please fill in all fields");
return;
}
// Validate EVM address
if (!isAddress(recipient)) {
alert("Invalid recipient address");
return;
}
try {
setIsLoading(true);
const tx = await wallet.send(recipient, token, amount);
setTxLink(tx.explorerLink);
} catch (error) {
console.error("Transfer error:", error);
alert("Transfer failed: " + error.message);
} finally {
setIsLoading(false);
}
}
return (
<div className="flex flex-col gap-3 p-5 bg-white rounded-xl border">
<h2 className="text-lg font-medium">Transfer funds</h2>
{/* Token selection */}
<div className="flex gap-4">
<label className="flex items-center gap-2">
<input
type="radio"
checked={token === "eth"}
onChange={() => setToken("eth")}
/>
<span>ETH</span>
</label>
<label className="flex items-center gap-2">
<input
type="radio"
checked={token === "usdxm"}
onChange={() => setToken("usdxm")}
/>
<span>USDXM</span>
</label>
</div>
{/* Amount input */}
<input
type="number"
className="px-3 py-2 border rounded-md"
placeholder="0.00"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
{/* Recipient input */}
<input
type="text"
className="px-3 py-2 border rounded-md"
placeholder="Recipient address"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>
{/* Transfer button */}
<button
className="py-2 px-4 bg-blue-500 text-white rounded-md disabled:opacity-50"
onClick={handleTransfer}
disabled={isLoading}
>
{isLoading ? "Transferring..." : "Transfer"}
</button>
{/* Transaction link */}
{txLink && (
<a
href={txLink}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-blue-500"
>
View on Explorer
</a>
)}
</div>
);
}
Solana Transfer
import { useState } from "react";
import { useWallet } from "@crossmint/client-sdk-react-ui";
import { PublicKey } from "@solana/web3.js";
export function SolanaTransferFunds() {
const { wallet } = useWallet();
const [token, setToken] = useState<"sol" | "usdxm">("sol");
const [recipient, setRecipient] = useState("");
const [amount, setAmount] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [txLink, setTxLink] = useState<string | null>(null);
const isValidSolanaAddress = (address: string) => {
try {
new PublicKey(address);
return true;
} catch {
return false;
}
};
async function handleTransfer() {
if (!wallet || !recipient || !amount) {
alert("Please fill in all fields");
return;
}
if (!isValidSolanaAddress(recipient)) {
alert("Invalid Solana address");
return;
}
try {
setIsLoading(true);
const tx = await wallet.send(recipient, token, amount);
setTxLink(tx.explorerLink);
} catch (error) {
console.error("Transfer error:", error);
alert("Transfer failed: " + error.message);
} finally {
setIsLoading(false);
}
}
return (
<div className="flex flex-col gap-3 p-5 bg-white rounded-xl border">
<h2 className="text-lg font-medium">Transfer funds</h2>
<div className="flex gap-4">
<label className="flex items-center gap-2">
<input
type="radio"
checked={token === "sol"}
onChange={() => setToken("sol")}
/>
<span>SOL</span>
</label>
<label className="flex items-center gap-2">
<input
type="radio"
checked={token === "usdxm"}
onChange={() => setToken("usdxm")}
/>
<span>USDXM</span>
</label>
</div>
<input
type="number"
className="px-3 py-2 border rounded-md"
placeholder="0.00"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<input
type="text"
className="px-3 py-2 border rounded-md"
placeholder="Recipient address"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>
<button
className="py-2 px-4 bg-blue-500 text-white rounded-md disabled:opacity-50"
onClick={handleTransfer}
disabled={isLoading}
>
{isLoading ? "Transferring..." : "Transfer"}
</button>
{txLink && (
<a
href={txLink}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-blue-500"
>
View on Solscan
</a>
)}
</div>
);
}
Advanced Operations
Wallet Activity
const activity = await wallet.experimental_activity();
console.log("Recent transactions:", activity.events);
Delegated Signers
Add external signers to your wallet:// Add a delegated signer
await wallet.addDelegatedSigner({
signer: "0x1234..."
});
// Get all delegated signers
const signers = await wallet.delegatedSigners();
console.log("Delegated signers:", signers);
Custom Transactions
import { SolanaWallet } from "@crossmint/wallets-sdk";
const solanaWallet = SolanaWallet.from(wallet);
const tx = await solanaWallet.sendTransaction({
transaction: "<serialized-or-non-serialized-transaction>"
});
console.log(tx.explorerLink);
Complete Example
Here’s a complete wallet management component:import { useAuth, useWallet } from "@crossmint/client-sdk-react-ui";
import { WalletBalance } from "./WalletBalance";
import { EVMTransferFunds } from "./EVMTransfer";
export function WalletManager() {
const { login, logout, user, status: authStatus } = useAuth();
const { wallet, status: walletStatus } = useWallet();
if (authStatus === "logged-out") {
return (
<div className="flex flex-col gap-4">
<h1>Login to access your wallet</h1>
<button onClick={login}>Login with Crossmint</button>
</div>
);
}
if (walletStatus === "in-progress") {
return <p>Loading wallet...</p>;
}
return (
<div className="flex flex-col gap-6">
<div>
<h1 className="text-2xl font-bold">Your Wallet</h1>
<p>User: {user?.email}</p>
</div>
{wallet && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Wallet info and balance */}
<div className="p-5 bg-white rounded-xl border">
<h2 className="text-lg font-medium mb-3">Wallet Info</h2>
<p className="text-sm text-gray-500 mb-2">
{wallet.address}
</p>
<WalletBalance />
<button
onClick={logout}
className="mt-4 px-4 py-2 bg-red-500 text-white rounded"
>
Logout
</button>
</div>
{/* Transfer component */}
<EVMTransferFunds />
</div>
)}
</div>
);
}
Next Steps
Authentication
Learn about wallet authentication
Next.js Example
Full Next.js integration
React Native
Build mobile wallet apps
API Reference
Explore the useWallet hook
Troubleshooting
Wallet not loading
Wallet not loading
- Verify your API key is correct
- Check that the user is authenticated
- Ensure the chain is supported
Transfer failing
Transfer failing
- Verify the wallet has sufficient balance
- Check that the recipient address is valid
- Ensure the token is supported on the chain
Balance not updating
Balance not updating
Balances may take a few seconds to update after transactions. Try refreshing the page or re-fetching the balance.