Overview
useChipiWallet is a convenience hook that simplifies wallet management by combining multiple operations into one:
- Wallet fetching (replaces
useGetWallet)
- Wallet creation (replaces
useCreateWallet)
- Balance checking (replaces
useGetTokenBalance)
Use this hook when you want a streamlined API. Use the individual hooks (useGetWallet, useCreateWallet, useGetTokenBalance) when you need more granular control.
Quick Start
import { useAuth } from "@clerk/nextjs";
import { useChipiWallet } from "@chipi-stack/nextjs";
function WalletComponent() {
const { userId, getToken } = useAuth();
const {
wallet,
hasWallet,
balance,
formattedBalance,
createWallet,
isCreating,
isLoadingWallet,
} = useChipiWallet({
externalUserId: userId,
getBearerToken: getToken,
});
if (isLoadingWallet) return <div>Loading...</div>;
if (!hasWallet) {
return (
<button
onClick={() => createWallet({ encryptKey: "1234", chain: "STARKNET" })}
disabled={isCreating}
>
{isCreating ? "Creating..." : "Create Wallet"}
</button>
);
}
return (
<div>
<p>Address: {wallet?.shortAddress}</p>
<p>Balance: ${formattedBalance} USDC</p>
</div>
);
}
Configuration Options
interface UseChipiWalletConfig {
// Required
externalUserId: string | null | undefined;
getBearerToken: () => Promise<string | null | undefined>;
// Optional
defaultToken?: ChainToken; // Default: "USDC"
enabled?: boolean; // Default: true
}
| Option | Type | Default | Description |
|---|
externalUserId | string | null | Required | User ID from your auth provider (Clerk, Firebase, etc.) |
getBearerToken | () => Promise<string> | Required | Function to get the auth token |
defaultToken | ChainToken | "USDC" | Token to fetch balance for |
enabled | boolean | true | Whether to fetch wallet on mount |
Return Values
Wallet Data
| Property | Type | Description |
|---|
wallet | ChipiWalletData | null | undefined | Wallet data with computed properties |
hasWallet | boolean | Whether the user has a wallet |
isLoadingWallet | boolean | Wallet fetch loading state |
walletError | Error | null | Any error from fetching |
Balance Data
| Property | Type | Description |
|---|
balance | GetTokenBalanceResponse | undefined | Raw balance data |
formattedBalance | string | Formatted balance (e.g., “1,234.56”) |
isLoadingBalance | boolean | Balance fetch loading state |
Create Wallet
| Property | Type | Description |
|---|
createWallet | (params) => Promise<CreateWalletResponse> | Create a new wallet |
isCreating | boolean | Creation loading state |
createdWallet | CreateWalletResponse | undefined | Last created wallet data |
Actions
| Method | Description |
|---|
refetchWallet() | Refetch wallet data |
refetchBalance() | Refetch balance data |
refetchAll() | Refetch both wallet and balance |
Computed Wallet Properties
The wallet object includes these computed properties:
interface ChipiWalletData extends GetWalletResponse {
supportsSessionKeys: boolean; // true for CHIPI wallets
shortAddress: string; // Truncated address for display
}
Example: Full Implementation with Clerk
"use client";
import { useState } from "react";
import { useAuth, SignInButton, SignedIn, SignedOut } from "@clerk/nextjs";
import { useChipiWallet } from "@chipi-stack/nextjs";
export function WalletDashboard() {
const { userId, getToken } = useAuth();
const [pin, setPin] = useState("");
const {
wallet,
hasWallet,
formattedBalance,
createWallet,
isCreating,
isLoadingWallet,
refetchAll,
} = useChipiWallet({
externalUserId: userId,
getBearerToken: getToken,
});
const handleCreateWallet = async (walletType: "CHIPI" | "READY") => {
if (!pin || pin.length < 4) {
alert("Please enter a 4-digit PIN");
return;
}
try {
const result = await createWallet({
encryptKey: pin,
chain: "STARKNET",
walletType,
});
alert(`Wallet created! TX: ${result.txHash}`);
} catch (err) {
console.error(err);
alert("Failed to create wallet");
}
};
return (
<div className="p-6 space-y-6">
<SignedOut>
<SignInButton mode="modal">
<button className="btn">Sign In</button>
</SignInButton>
</SignedOut>
<SignedIn>
{isLoadingWallet ? (
<p>Loading wallet...</p>
) : hasWallet ? (
<div className="space-y-4">
<div>
<p className="text-sm text-gray-500">Wallet Address</p>
<p className="font-mono">{wallet?.shortAddress}</p>
</div>
<div>
<p className="text-sm text-gray-500">Balance</p>
<p className="text-3xl font-bold">${formattedBalance} USDC</p>
</div>
<div>
<p className="text-sm text-gray-500">Session Keys</p>
<p>{wallet?.supportsSessionKeys ? "Supported" : "Not Supported"}</p>
</div>
<button onClick={() => refetchAll()} className="btn-secondary">
Refresh
</button>
</div>
) : (
<div className="space-y-4">
<input
type="password"
placeholder="Enter 4-digit PIN"
value={pin}
onChange={(e) => setPin(e.target.value)}
maxLength={4}
className="input"
/>
<div className="flex gap-2">
<button
onClick={() => handleCreateWallet("CHIPI")}
disabled={isCreating}
className="btn-primary"
>
Create Chipi Wallet
</button>
<button
onClick={() => handleCreateWallet("READY")}
disabled={isCreating}
className="btn-secondary"
>
Create Argent Wallet
</button>
</div>
</div>
)}
</SignedIn>
</div>
);
}
Wallet Types
When creating a wallet, you can specify the type:
| Type | Session Keys | Description |
|---|
CHIPI | ✅ Yes | OpenZeppelin account with SNIP-9 session key support |
READY | ❌ No | Argent X compatible wallet |
// Create a CHIPI wallet (supports session keys)
await createWallet({
encryptKey: pin,
chain: "STARKNET",
walletType: "CHIPI"
});
// Create a READY wallet (no session keys)
await createWallet({
encryptKey: pin,
chain: "STARKNET",
walletType: "READY"
});
Migration from Individual Hooks
If you’re currently using separate hooks, here’s how to migrate:
Before
const { data: wallet, isLoading } = useGetWallet({
params: { externalUserId: userId },
getBearerToken: getToken,
enabled: !!userId,
});
const { createWalletAsync } = useCreateWallet();
const { data: balance } = useGetTokenBalance({
params: { walletPublicKey: wallet?.publicKey, ... },
getBearerToken: getToken,
enabled: !!wallet?.publicKey,
});
After
const {
wallet,
hasWallet,
balance,
formattedBalance,
createWallet,
isLoadingWallet,
} = useChipiWallet({
externalUserId: userId,
getBearerToken: getToken,
});
useChipiWallet automatically handles the dependency chain - it only fetches balance after the wallet is loaded.