diff --git a/docs/blockchain-development-tutorials/integrations/crossmint/smart-wallets.md b/docs/blockchain-development-tutorials/integrations/crossmint/smart-wallets.md new file mode 100644 index 0000000000..a2ae56c49f --- /dev/null +++ b/docs/blockchain-development-tutorials/integrations/crossmint/smart-wallets.md @@ -0,0 +1,914 @@ +--- +sidebar_position: 4 +title: Crossmint Smart Wallets +description: Learn how to integrate Crossmint Smart Wallets to create seamless Web3 experiences with email-based authentication on Flow. +keywords: + - tutorials + - guides + - flow + - smart wallets + - crossmint + - authentication + - wallet infrastructure + - web3 + - email authentication + - gasless transactions +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Crossmint Smart Wallets Integration Guide + +Traditional blockchain wallets create significant friction for mainstream users. Managing seed phrases, understanding gas fees, and connecting multiple wallets are barriers that prevent widespread Web3 adoption. Crossmint Smart Wallets solves these problems by providing **enterprise-grade wallet infrastructure** that enables Web2-like user experiences without compromising on security or decentralization. + +With Crossmint Smart Wallets, you can: +- **Eliminate wallet complexity** with email and social login authentication +- **Remove onboarding friction** by automatically creating wallets for users +- **Support multiple authentication methods** including email, Google, passkeys, and external wallets +- **Enable gasless transactions** to improve user experience +- **Build on Flow** with full support for both mainnet and testnet environments +- **Scale with confidence** using infrastructure trusted by Fortune 500 companies + +This tutorial will guide you through integrating Crossmint Smart Wallets into your Flow application. You'll learn how to set up authentication, automatically create wallets, check balances, transfer tokens, and display transaction historyall with a familiar Web2-style developer experience. + +:::info + +Crossmint provides flexible wallet solutions across 50+ blockchains including Flow. This tutorial focuses on the **React implementation** for web applications, but Crossmint also supports Node.js, React Native, Swift (iOS), and Kotlin (Android) platforms. + +::: + +## Objectives + +After completing this guide, you'll be able to: + +- Configure a Crossmint account with proper API keys and permissions +- Implement email and social authentication for automatic wallet creation +- Display wallet information including address, balance, and ownership details +- Execute token transfers on Flow using Crossmint's SDK +- Build an activity feed showing transaction history +- Handle authentication states and error scenarios properly +- Deploy your Crossmint-powered application to production + +## Prerequisites + +Before starting this tutorial, you should have: + +- **Development Environment**: Node.js and npm/yarn/pnpm installed +- **React Knowledge**: Familiarity with React hooks and component patterns +- **Next.js or Create-React-App**: A React application ready for integration +- **Basic Blockchain Concepts**: Understanding of wallet addresses and token transfers (helpful but not required) + +## Setting Up Your Crossmint Account + +You need to create a Crossmint account and configure API access before implementing wallet functionality. + +### Step 1. Create Your Crossmint Account + +Sign up on the [Crossmint Console](https://www.crossmint.com/console) to establish an account. For development and testing, use the [Staging Console](https://staging.crossmint.com/console) instead. + +:::tip + +Always use the staging environment during development. Staging supports testnet blockchains only, while production supports mainnet deployments. + +::: + +### Step 2. Create a New Project + +After logging into the console: + +1. Click **Create New Project** +2. Enter a project name (e.g., "Flow DApp") +3. Select your project type (Web Application recommended) +4. Save your project settings + +### Step 3. Generate API Keys + +Navigate to your project dashboard to create a client-side API key: + +1. Go to the **API Keys** section +2. Click **Create New API Key** +3. Select **Client API Key** (not server key) +4. Enable the following scopes: + - `users.create` - Create new users + - `users.read` - Read user information + - `wallets.read` - Read wallet data + - `wallets.create` - Create new wallets + - `wallets:transactions.create` - Create transactions + - `wallets:transactions.sign` - Sign transactions + - `wallets:balance.read` - Read balance information + - `wallets.fund` - Fund wallets (staging/development only) + +5. Copy the generated API key to your clipboard + +:::warning + +Keep your API keys secure! Never commit them to version control. Use environment variables to store sensitive credentials. + +::: + +### Step 4. Configure Environment Variables + +Create a `.env` or `.env.local` file in your project root: + +```bash +NEXT_PUBLIC_CROSSMINT_API_KEY=your_api_key_here +NEXT_PUBLIC_CHAIN=flow-testnet +``` + +For production deployments, update to: + +```bash +NEXT_PUBLIC_CROSSMINT_API_KEY=your_production_api_key +NEXT_PUBLIC_CHAIN=flow +``` + +## Implementing Crossmint Smart Wallets + +With your Crossmint account configured, you can now integrate wallet functionality into your React application. + +### Step 1. Install Dependencies + +Install the Crossmint React SDK: + + + + ```bash + pnpm add @crossmint/client-sdk-react-ui + ``` + + + ```bash + bun add @crossmint/client-sdk-react-ui + ``` + + + ```bash + yarn add @crossmint/client-sdk-react-ui + ``` + + + ```bash + npm install @crossmint/client-sdk-react-ui + ``` + + + +### Step 2. Configure Crossmint Providers + +Crossmint requires three providers to be set up in a specific hierarchy. These providers handle API configuration, authentication, and wallet management. + + + + Create a new file `app/providers.tsx`: + + ```tsx + "use client"; + + import { + CrossmintProvider, + CrossmintAuthProvider, + CrossmintWalletProvider, + } from "@crossmint/client-sdk-react-ui"; + + if (!process.env.NEXT_PUBLIC_CROSSMINT_API_KEY) { + throw new Error("NEXT_PUBLIC_CROSSMINT_API_KEY is not set"); + } + + const chain = (process.env.NEXT_PUBLIC_CHAIN ?? "flow-testnet") as any; + + export function Providers({ children }: { children: React.ReactNode }) { + return ( + + + + {children} + + + + ); + } + ``` + + Then wrap your app in `app/layout.tsx`: + + ```tsx + import { Providers } from "./providers"; + + export default function RootLayout({ + children, + }: { + children: React.ReactNode; + }) { + return ( + + + {children} + + + ); + } + ``` + + + Update your `src/index.tsx` or `src/index.jsx`: + + ```tsx + import React from 'react'; + import ReactDOM from 'react-dom/client'; + import App from './App'; + import { + CrossmintProvider, + CrossmintAuthProvider, + CrossmintWalletProvider, + } from "@crossmint/client-sdk-react-ui"; + + const chain = process.env.REACT_APP_CHAIN ?? "flow-testnet"; + const apiKey = process.env.REACT_APP_CROSSMINT_API_KEY; + + if (!apiKey) { + throw new Error("REACT_APP_CROSSMINT_API_KEY is not set"); + } + + const root = ReactDOM.createRoot(document.getElementById('root')); + root.render( + + + + + + + + + + ); + ``` + + + +**Provider Configuration Options:** + +- **CrossmintProvider**: Top-level provider requiring only your API key +- **CrossmintAuthProvider**: Manages authentication with configurable options: + - `authModalTitle`: Title displayed in the authentication modal + - `loginMethods`: Array of enabled authentication methods (`"email"`, `"google"`, `"apple"`, `"twitter"`, `"farcaster"`) + - `appearance`: Customize UI colors and styling +- **CrossmintWalletProvider**: Handles wallet creation and management: + - `createOnLogin.chain`: Target blockchain (e.g., `"flow"`, `"flow-testnet"`) + - `createOnLogin.signer.type`: Authentication method for wallet signing (`"email"`, `"passkey"`) + +:::info + +The `createOnLogin` configuration enables **automatic wallet creation**. When a user logs in for the first time, Crossmint automatically provisions a wallet on the specified chain. No additional setup required! + +::: + +### Step 3. Implement Authentication + +Create login and logout components using Crossmint's `useAuth` hook. + +**LoginButton.tsx:** + +```tsx +"use client"; + +import { useAuth } from "@crossmint/client-sdk-react-ui"; + +export function LoginButton() { + const { login } = useAuth(); + + return ( + + ); +} +``` + +**LogoutButton.tsx:** + +```tsx +"use client"; + +import { useAuth } from "@crossmint/client-sdk-react-ui"; + +export function LogoutButton() { + const { logout } = useAuth(); + + return ( + + ); +} +``` + +**Header.tsx (Conditional rendering):** + +```tsx +"use client"; + +import { useAuth, useWallet } from "@crossmint/client-sdk-react-ui"; +import { LoginButton } from "./LoginButton"; +import { LogoutButton } from "./LogoutButton"; + +export function Header() { + const { status: authStatus } = useAuth(); + const { wallet } = useWallet(); + + const isLoggedIn = wallet != null && authStatus === "logged-in"; + + return ( +
+
+

Flow DApp

+ {isLoggedIn ? : } +
+
+ ); +} +``` + +### Step 4. Display Wallet Information + +Create a component to show wallet details using the `useWallet` hook. + +**WalletInfo.tsx:** + +```tsx +"use client"; + +import { useState } from "react"; +import { useAuth, useWallet } from "@crossmint/client-sdk-react-ui"; + +export function WalletInfo() { + const { wallet, status } = useWallet(); + const { user } = useAuth(); + const [copied, setCopied] = useState(false); + + if (status === "in-progress") { + return ( +
+
Loading wallet...
+
+ ); + } + + if (!wallet) { + return null; + } + + const formatAddress = (address: string) => { + return `${address.slice(0, 6)}...${address.slice(-6)}`; + }; + + const handleCopy = async () => { + await navigator.clipboard.writeText(wallet.address); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+

Wallet Details

+ +
+
+
Address
+
+ + {formatAddress(wallet.address)} + + +
+
+ +
+
Chain
+
{wallet.chain}
+
+ +
+
Owner
+
{user?.email || wallet.owner}
+
+
+
+ ); +} +``` + +### Step 5. Display Wallet Balance + +Fetch and display the wallet's token balance using the `wallet.balances()` method. + +**WalletBalance.tsx:** + +```tsx +"use client"; + +import { useEffect, useState } from "react"; +import { Balances, useWallet } from "@crossmint/client-sdk-react-ui"; + +export function WalletBalance() { + const { wallet } = useWallet(); + const [balances, setBalances] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + async function fetchBalances() { + if (!wallet) return; + + try { + setIsLoading(true); + const balances = await wallet.balances(); + setBalances(balances); + } catch (error) { + console.error("Error fetching wallet balances:", error); + } finally { + setIsLoading(false); + } + } + + fetchBalances(); + }, [wallet]); + + if (isLoading) { + return ( +
+
Loading balance...
+
+ ); + } + + const nativeBalance = balances?.nativeToken?.amount + ? Number(balances.nativeToken.amount).toFixed(4) + : "0.0000"; + + return ( +
+

Balance

+ +
+
+
+ {balances?.nativeToken?.symbol || "FLOW"} +
+
{nativeBalance}
+
+ + {balances?.tokens && balances.tokens.length > 0 && ( +
+
Tokens
+ {balances.tokens.map((token, index) => ( +
+ {token.symbol} + + {Number(token.amount).toFixed(2)} + +
+ ))} +
+ )} +
+
+ ); +} +``` + +### Step 6. Implement Token Transfers + +Create a component for transferring tokens using the `wallet.send()` method. + +**TransferTokens.tsx:** + +```tsx +"use client"; + +import { useState } from "react"; +import { useWallet } from "@crossmint/client-sdk-react-ui"; + +export function TransferTokens() { + const { wallet } = useWallet(); + const [recipient, setRecipient] = useState(""); + const [amount, setAmount] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [explorerLink, setExplorerLink] = useState(null); + const [error, setError] = useState(null); + + async function handleTransfer() { + if (!wallet || !recipient || !amount) { + setError("Please fill in all fields"); + return; + } + + try { + setIsLoading(true); + setError(null); + setExplorerLink(null); + + const txn = await wallet.send( + recipient, + "flow", // Token symbol - use native FLOW token + amount + ); + + setExplorerLink(txn.explorerLink); + + // Reset form + setRecipient(""); + setAmount(""); + } catch (err) { + console.error("Transfer error:", err); + + if (err instanceof Error && err.name === "AuthRejectedError") { + // User cancelled the transaction - don't show error + return; + } + + setError(err instanceof Error ? err.message : "Transfer failed"); + } finally { + setIsLoading(false); + } + } + + return ( +
+

Transfer Tokens

+ +
+
+ + setAmount(e.target.value)} + placeholder="0.00" + className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + disabled={isLoading} + /> +
+ +
+ + setRecipient(e.target.value)} + placeholder="0x..." + className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono text-sm" + disabled={isLoading} + /> +
+ + {error && ( +
+ {error} +
+ )} + + {explorerLink && ( +
+
+ Transaction successful! +
+ + View on Explorer � + +
+ )} + + +
+
+ ); +} +``` + +:::tip + +The `wallet.send()` method throws an `AuthRejectedError` when users cancel the transaction. Handle this separately to avoid showing unnecessary error messages. + +::: + +### Step 7. Build Activity Feed + +Display transaction history using the `wallet.experimental_activity()` method with polling for real-time updates. + +**ActivityFeed.tsx:** + +```tsx +"use client"; + +import { useEffect, useState } from "react"; +import { type Activity, useWallet } from "@crossmint/client-sdk-react-ui"; + +export function ActivityFeed() { + const { wallet } = useWallet(); + const [activity, setActivity] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + if (!wallet) return; + + const fetchActivity = async () => { + try { + const activity = await wallet.experimental_activity(); + setActivity(activity); + setIsLoading(false); + } catch (error) { + console.error("Failed to fetch activity:", error); + setIsLoading(false); + } + }; + + // Initial fetch + fetchActivity(); + + // Poll every 8 seconds for updates + const interval = setInterval(fetchActivity, 8000); + + return () => clearInterval(interval); + }, [wallet]); + + const formatAddress = (address: string) => { + return `${address.slice(0, 6)}...${address.slice(-6)}`; + }; + + const formatTimestamp = (timestamp: number) => { + // Handle both seconds and milliseconds + const date = new Date( + timestamp < 10000000000 ? timestamp * 1000 : timestamp + ); + const now = new Date(); + const diffInMs = now.getTime() - date.getTime(); + + if (diffInMs < 0) return "just now"; + + const diffInMinutes = Math.floor(diffInMs / (1000 * 60)); + const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60)); + const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24)); + + if (diffInMinutes < 1) return "just now"; + else if (diffInMinutes < 60) return `${diffInMinutes}m ago`; + else if (diffInHours < 24) return `${diffInHours}h ago`; + else return `${diffInDays}d ago`; + }; + + if (isLoading) { + return ( +
+
Loading activity...
+
+ ); + } + + return ( +
+

Recent Activity

+ + {activity?.events && activity.events.length > 0 ? ( +
+ {activity.events.map((event, index) => { + const isIncoming = + event.to_address?.toLowerCase() === wallet?.address.toLowerCase(); + + return ( +
+
+
+ + {isIncoming ? "Received" : "Sent"} + + + {formatTimestamp(event.timestamp)} + +
+
+ {isIncoming + ? `From ${formatAddress(event.from_address)}` + : `To ${formatAddress(event.to_address)}` + } +
+
+
+
+ {isIncoming ? "+" : "-"}{event.amount} +
+
+ {event.token_symbol || "FLOW"} +
+
+
+ ); + })} +
+ ) : ( +
+

No transactions yet

+

Your activity will appear here

+
+ )} +
+ ); +} +``` + +:::warning + +The `experimental_activity()` method is experimental and may change in future SDK versions. Always handle errors gracefully and provide fallback UI. + +::: + +### Step 8. Create Main Dashboard + +Combine all components into a cohesive dashboard with proper state management. + +**Dashboard.tsx:** + +```tsx +"use client"; + +import { WalletInfo } from "./WalletInfo"; +import { WalletBalance } from "./WalletBalance"; +import { TransferTokens } from "./TransferTokens"; +import { ActivityFeed } from "./ActivityFeed"; + +export function Dashboard() { + return ( +
+
+
+ + +
+ +
+ + +
+
+
+ ); +} +``` + +**page.tsx (Main application):** + +```tsx +"use client"; + +import { useAuth, useWallet } from "@crossmint/client-sdk-react-ui"; +import { Header } from "@/components/Header"; +import { Dashboard } from "@/components/Dashboard"; +import { LoginButton } from "@/components/LoginButton"; + +export default function Home() { + const { wallet, status: walletStatus } = useWallet(); + const { status: authStatus } = useAuth(); + + const isLoggedIn = wallet != null && authStatus === "logged-in"; + const isLoading = walletStatus === "in-progress" || authStatus === "initializing"; + + return ( +
+
+ +
+ {isLoading ? ( +
+
+
+

Initializing wallet...

+
+
+ ) : isLoggedIn ? ( + + ) : ( +
+
+

Welcome to Flow

+

+ Sign in to access your wallet and start transacting on Flow blockchain +

+ +
+
+ )} +
+
+ ); +} +``` + +--- + +## Additional Platform Support + +While this tutorial focuses on React for web applications, Crossmint provides SDKs for multiple platforms: + +### Node.js (Backend) +For server-side wallet creation and management, use the Node.js SDK: +- [Node.js Quickstart Documentation](https://docs.crossmint.com/wallets/quickstarts/nodejs) + +### React Native (Mobile) +For iOS and Android mobile applications: +- [React Native Quickstart Documentation](https://docs.crossmint.com/wallets/quickstarts/react-native) + +### Swift (iOS Native) +For native iOS development: +- Contact [Crossmint Sales](https://www.crossmint.com/contact/sales) for access + +### Kotlin (Android Native) +For native Android development: +- Contact [Crossmint Sales](https://www.crossmint.com/contact/sales) for access + +--- + +## Conclusion + +In this tutorial, you successfully integrated Crossmint Smart Wallets to enable seamless blockchain experiences on Flow. You learned how to implement email-based authentication, automatically create wallets for users, display balances, execute token transfers, and show transaction historyall without requiring users to understand complex blockchain concepts like seed phrases or gas fees. + +Now that you have completed the tutorial, you should be able to: + +- Configure Crossmint accounts with proper API keys and permissions +- Implement multiple authentication methods including email and social login +- Automatically create and manage wallets for users +- Display wallet information, balances, and transaction history +- Execute token transfers with proper error handling +- Build production-ready applications with enterprise-grade wallet infrastructure + +Crossmint's wallet infrastructure, combined with Flow's high-performance blockchain, provides a powerful foundation for building user-friendly Web3 applications. By eliminating wallet complexity and onboarding friction, you can create experiences that attract mainstream users while maintaining the security and transparency benefits of blockchain technology. + +## Next Steps + +- Explore [Crossmint's NFT Minting Platform](https://docs.crossmint.com/nft-minting/overview) to add NFT functionality +- Learn about [Payment Checkout](https://docs.crossmint.com/payments/overview) for credit card and crypto payments +- Implement [Passkey Authentication](https://docs.crossmint.com/wallets/signers/passkey) for enhanced security +- Review [Flow Smart Contract Development](../../cadence/) to build custom on-chain logic +- Join the [Flow Discord](https://discord.gg/flow) to connect with other developers + +[Crossmint Console]: https://www.crossmint.com/console +[Staging Console]: https://staging.crossmint.com/console +[Crossmint Documentation]: https://docs.crossmint.com/ +[Crossmint Wallets SDK]: https://github.com/Crossmint/crossmint-sdk +[Flow Discord]: https://discord.gg/flow