Overview
useChipiSession provides a unified API for managing session keys, enabling gasless transactions without requiring the owner’s signature for each operation.
This hook combines all session-related operations:
- Session creation (local keypair generation)
- Session registration (on-chain)
- Transaction execution (using session key)
- Session revocation (on-chain)
- Status checking (remaining calls, expiration)
Session keys only work with CHIPI wallets. READY wallets do not support session keys.
Prerequisites
Before using useChipiSession, you need:
- A CHIPI wallet (check
wallet.walletType === "CHIPI" or wallet.supportsSessionKeys)
- The user’s encryption key (PIN)
- An authentication token (from Clerk, Firebase, etc.)
Session Lifecycle
| State | Description |
|---|
none | No session exists |
created | Session created locally, not yet registered on-chain |
active | Session registered and usable |
expired | Session has expired (time or call limit) |
revoked | Session has been revoked on-chain |
Quick Start
import { useChipiWallet, useChipiSession } from "@chipi-stack/chipi-expo";
import { View, Text, TouchableOpacity, ActivityIndicator } from "react-native";
function SessionComponent() {
const { userId, getToken } = useYourAuthProvider();
const [pin, setPin] = useState("");
const { wallet } = useChipiWallet({
externalUserId: userId,
getBearerToken: getToken,
});
const {
session,
sessionState,
hasActiveSession,
remainingCalls,
createSession,
registerSession,
executeWithSession,
isCreating,
isRegistering,
isExecuting,
} = useChipiSession({
wallet,
encryptKey: pin,
getBearerToken: getToken,
});
// Setup session (create + register)
const handleSetup = async () => {
await createSession();
await registerSession();
};
// Execute a transfer using the session
const handleTransfer = async () => {
await executeWithSession([{
contractAddress: "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8",
entrypoint: "transfer",
calldata: [recipientAddress, amount, "0x0"],
}]);
};
return (
<View style={{ padding: 20, gap: 16 }}>
<Text>Session: {sessionState}</Text>
{hasActiveSession && <Text>Calls remaining: {remainingCalls}</Text>}
{!hasActiveSession ? (
<TouchableOpacity
onPress={handleSetup}
disabled={isCreating || isRegistering}
>
<Text>Setup Session</Text>
</TouchableOpacity>
) : (
<TouchableOpacity onPress={handleTransfer} disabled={isExecuting}>
<Text>Transfer (Gasless)</Text>
</TouchableOpacity>
)}
</View>
);
}
Configuration Options
interface UseChipiSessionConfig {
// Required
wallet: SessionWallet | null | undefined;
encryptKey: string;
getBearerToken: () => Promise<string | null | undefined>;
// Optional
storedSession?: SessionKeyData | null;
onSessionCreated?: (session: SessionKeyData) => void | Promise<void>;
defaultDurationSeconds?: number; // Default: 21600 (6 hours)
defaultMaxCalls?: number; // Default: 1000
autoCheckStatus?: boolean; // Default: true
}
| Option | Type | Default | Description |
|---|
wallet | SessionWallet | Required | Wallet data (must be CHIPI type) |
encryptKey | string | Required | User’s PIN for signing |
getBearerToken | () => Promise<string> | Required | Auth token function |
storedSession | SessionKeyData | null | Previously stored session |
onSessionCreated | (session) => void | - | Callback to persist new session |
defaultDurationSeconds | number | 21600 | Session duration (6 hours) |
defaultMaxCalls | number | 1000 | Max calls per session |
autoCheckStatus | boolean | true | Auto-fetch on-chain status |
Return Values
Session Data
| Property | Type | Description |
|---|
session | SessionKeyData | null | Current session data |
sessionStatus | SessionDataResponse | On-chain session status |
sessionState | SessionState | Lifecycle state |
hasActiveSession | boolean | Whether session is usable |
isSessionExpired | boolean | Whether session has expired |
remainingCalls | number | undefined | Calls left in session |
supportsSession | boolean | Whether wallet supports sessions |
Actions
| Method | Returns | Description |
|---|
createSession(config?) | Promise<SessionKeyData> | Create new session locally |
registerSession(config?) | Promise<string> | Register on-chain (returns txHash) |
revokeSession() | Promise<string> | Revoke on-chain (returns txHash) |
executeWithSession(calls) | Promise<string> | Execute calls (returns txHash) |
clearSession() | void | Clear local session state |
refetchStatus() | Promise<void> | Refresh on-chain status |
Loading States
| Property | Description |
|---|
isCreating | Creating session locally |
isRegistering | Registering session on-chain |
isRevoking | Revoking session on-chain |
isExecuting | Executing transaction |
isLoadingStatus | Fetching on-chain status |
Persisting Sessions with SecureStore
In Expo, use expo-secure-store to safely persist session data:
import * as SecureStore from 'expo-secure-store';
import { useState, useEffect } from "react";
import { useChipiWallet, useChipiSession } from "@chipi-stack/chipi-expo";
const SESSION_KEY = "chipi-session";
function PersistentSession() {
const { wallet } = useChipiWallet({ ... });
const [storedSession, setStoredSession] = useState(null);
// Load session on mount
useEffect(() => {
async function loadSession() {
const saved = await SecureStore.getItemAsync(SESSION_KEY);
if (saved) {
setStoredSession(JSON.parse(saved));
}
}
loadSession();
}, []);
const {
session,
hasActiveSession,
createSession,
registerSession,
} = useChipiSession({
wallet,
encryptKey: pin,
getBearerToken: getToken,
storedSession,
onSessionCreated: async (newSession) => {
// Persist to SecureStore
await SecureStore.setItemAsync(SESSION_KEY, JSON.stringify(newSession));
setStoredSession(newSession);
},
});
// Session is automatically restored on mount!
}
Using with Biometrics
Combine with useBiometrics hook for secure PIN-less UX:
import { useBiometrics, useChipiWallet, useChipiSession } from "@chipi-stack/chipi-expo";
function BiometricSession() {
const { authenticate, isAvailable } = useBiometrics();
const { wallet } = useChipiWallet({ ... });
// Store PIN securely and retrieve with biometrics
const [encryptKey, setEncryptKey] = useState("");
const {
hasActiveSession,
createSession,
registerSession,
executeWithSession,
} = useChipiSession({
wallet,
encryptKey,
getBearerToken: getToken,
});
const handleSetupWithBiometrics = async () => {
// Authenticate with Face ID / Touch ID
const success = await authenticate("Confirm to setup session");
if (!success) return;
// Retrieve stored PIN (implement your own secure storage)
const pin = await getStoredPin();
setEncryptKey(pin);
await createSession();
await registerSession();
};
// ...
}
Executing Transactions
The executeWithSession method accepts an array of Starknet calls:
// Single transfer
await executeWithSession([{
contractAddress: USDC_CONTRACT,
entrypoint: "transfer",
calldata: [recipient, amount, "0x0"],
}]);
// Batch multiple calls
await executeWithSession([
{
contractAddress: USDC_CONTRACT,
entrypoint: "approve",
calldata: [spender, amount, "0x0"],
},
{
contractAddress: DEX_CONTRACT,
entrypoint: "swap",
calldata: [...],
},
]);
Custom Session Configuration
Custom Duration
// Create session valid for 1 hour
await createSession({ durationSeconds: 3600 });
Custom Max Calls
// Register with 500 max calls
await registerSession({ maxCalls: 500 });
Error Handling
const { error, session } = useChipiSession({...});
// Check for errors
if (error) {
Alert.alert("Session Error", error.message);
}
// Handle specific errors
const handleSetup = async () => {
try {
await createSession();
await registerSession();
} catch (err) {
if (err.message.includes("does not support sessions")) {
Alert.alert("Error", "Please use a CHIPI wallet for sessions");
} else {
Alert.alert("Error", err.message);
}
}
};