Skip to main content

CavosProvider

The provider component that initializes the SDK and provides context to child components.
import { CavosProvider } from '@cavos/react';

<CavosProvider config={config} modal={modalConfig}>
  <App />
</CavosProvider>

Config Props

interface CavosConfig {
  appId: string;                    // Required: Your Cavos App ID
  network?: 'sepolia' | 'mainnet';  // Default: 'sepolia'
  paymasterApiKey?: string;         // Custom Cavos Paymaster API key
  paymasterUrl?: string;            // Custom paymaster endpoint URL
  starknetRpcUrl?: string;          // Custom Starknet RPC URL
  backendUrl?: string;              // Custom backend URL (default: https://cavos.xyz)
  enableLogging?: boolean;          // Enable debug logs (default: false)
  session?: SessionConfig;          // Session duration and default policy
  oauthWallet?: Partial<OAuthWalletConfig>; // Advanced: custom class hash / registry
  slot?: SlotConfig;                // Cartridge Slot (Katana) configuration
}

interface SlotConfig {
  rpcUrl: string;                   // Required: RPC URL of your Slot
  chainId?: string;                 // Hex of internal chain ID
  relayerAddress?: string;          // Optional: custom relayer
  relayerPrivateKey?: string;       // Optional: relayer private key
}

interface SessionConfig {
  sessionDuration?: number;         // Seconds (default: 86400 = 24h)
  renewalGracePeriod?: number;      // Seconds (default: 172800 = 48h)
  defaultPolicy?: SessionKeyPolicy; // Applied to all sessions
}
interface CavosModalConfig {
  appName?: string;
  appLogo?: string;
  providers?: ('google' | 'apple' | 'email')[];
  primaryColor?: string;
  theme?: 'light' | 'dark'; // Default: 'light'
  onSuccess?: (address: string) => void;
}
Example:
// app/providers.tsx
'use client';

import { CavosProvider, SessionKeyPolicy } from '@cavos/react';

const SESSION_POLICY: SessionKeyPolicy = {
  allowedContracts: [
    '0x04718f5a0Fc34cC1AF16A1cdee98fFB20C31f5cD61D6Ab07201858f4287c938D', // STRK
  ],
  spendingLimits: [
    {
      token: '0x04718f5a0Fc34cC1AF16A1cdee98fFB20C31f5cD61D6Ab07201858f4287c938D',
      limit: BigInt('10000000000000000000'), // 10 STRK (18 decimals)
    },
  ],
  maxCallsPerTx: 5,
};

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <CavosProvider
      config={{
        appId: process.env.NEXT_PUBLIC_CAVOS_APP_ID || '',
        network: 'sepolia',
        paymasterApiKey: process.env.NEXT_PUBLIC_CAVOS_PAYMASTER_API_KEY || '',
        session: { defaultPolicy: SESSION_POLICY },
      }}
      modal={{ appName: 'My App', theme: 'dark' }}
    >
      {children}
    </CavosProvider>
  );
}

useCavos Hook

Access SDK functionality from any child component:
import { useCavos } from '@cavos/react';

function MyComponent() {
  const {
    // --- State ---
    isAuthenticated,
    user,
    address,
    isLoading,
    hasActiveSession,
    walletStatus,
    sessionPublicKey,

    // --- Auth ---
    login,
    sendMagicLink,
    logout,

    // --- Transactions ---
    execute,
    executeOnSlot,
    signMessage,

    // --- Session Management ---
    registerCurrentSession,
    updateSessionPolicy,
    renewSession,
    revokeSession,
    emergencyRevokeAllSessions,
    exportSession,

    // --- Account ---
    isAccountDeployed,
    deployAccount,
    getBalance,
    getAssociatedWallets,
    switchWallet,

    // --- Utilities ---
    openModal,
    closeModal,
    getOnramp,

    // --- Raw SDK ---
    cavos,
  } = useCavos();

  return <div>...</div>;
}

State Properties

PropertyTypeDescription
isAuthenticatedbooleanUser has an active OAuth session
userUserInfo | nullUser info from OAuth (id, email, name, picture)
addressstring | nullWallet address (0x...)
isLoadingbooleanSDK is initializing or processing
hasActiveSessionbooleanUser is authenticated with a valid session
walletStatusWalletStatusDetailed wallet deployment and session state
sessionPublicKeystring | nullPublic key of the current session key (safe to display)

WalletStatus

interface WalletStatus {
  isDeploying: boolean;           // Account contract is being deployed
  isDeployed: boolean;            // Account contract is deployed on-chain
  isRegistering: boolean;         // Session key is being registered on-chain
  isSessionActive: boolean;       // Session key is registered and not expired
  isReady: boolean;               // Wallet fully set up — ready for transactions
  pendingDeployTxHash?: string;   // Deploy tx hash if confirmation timed out (for explorer links)
}
Example Usage:
const { walletStatus, address } = useCavos();

if (!address) {
  return <LoginButton />;
}

if (!walletStatus.isReady) {
  return (
    <div>
      {walletStatus.isDeploying && <p>Deploying wallet...</p>}
      {walletStatus.isRegistering && <p>Registering session...</p>}
      {walletStatus.pendingDeployTxHash && (
        <a href={`https://sepolia.starkscan.co/tx/${walletStatus.pendingDeployTxHash}`}>
          View pending deploy
        </a>
      )}
      <span className="loading" />
    </div>
  );
}

// Ready for transactions!
return <TransactionButtons />;
[!NOTE] walletStatus.isReady is true only when the account is deployed and the session is registered. Both steps happen automatically after login() — no manual calls needed.

Auth Methods

login(provider)

Start authentication flow — opens a new tab to Google/Apple OAuth:
await login('google');
await login('apple');
Send a magic link email. Resolves immediately after the email is sent; authentication completes in the background when the user clicks the link.
const { sendMagicLink, isAuthenticated, address } = useCavos();

await sendMagicLink('user@example.com');
// UI can show "Check your email..."
// The SDK polls localStorage — when the user clicks the link in another tab,
// isAuthenticated and address update automatically via onAuthChange.

logout()

Clear session and wallet state:
await logout();

Transaction Methods

execute(calls, options?)

Execute transactions. Handles session registration automatically on first call.
// Single call — gasless (default)
const txHash = await execute({
  contractAddress: '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',
  entrypoint: 'transfer',
  calldata: [recipientAddress, amountLow, amountHigh],
});

// Multiple calls (multicall)
const txHash = await execute([
  { contractAddress: tokenAddress, entrypoint: 'approve', calldata: [spender, amount, '0'] },
  { contractAddress: protocolAddress, entrypoint: 'deposit', calldata: [amount, '0'] },
]);

// User pays gas (requires session already registered)
const txHash = await execute(calls, { gasless: false });
Session Stategasless: truegasless: false
Not registeredJWT signature — registers + executes atomically ✅Throws ❌
Registered & activeSession signature via Paymaster ✅Session signature, wallet pays STRK ✅
Expired (within grace)Auto-renews, then executes ✅Auto-renews, then executes ✅
Expired (beyond grace)Throws SESSION_EXPIREDThrows SESSION_EXPIRED

signMessage(typedData)

Sign SNIP-12 typed data with the session key:
const signature = await signMessage({
  types: { StarknetDomain: [...], Message: [...] },
  primaryType: 'Message',
  domain: { name: 'MyApp', version: '1', chainId: '0x534e5f5345504f4c4941' },
  message: { content: 'Hello!' },
});
// Returns [SESSION_V1_magic, r, s, session_key] — ready for on-chain is_valid_signature

Session Management

registerCurrentSession()

Explicitly register the session key on-chain using the current JWT. Normally called automatically — only needed for advanced flows.
const txHash = await registerCurrentSession();
[!IMPORTANT] Call updateSessionPolicy(policy) before registerCurrentSession() if you changed the policy after login. See the policy sync rule below.

updateSessionPolicy(policy)

Update the session policy that will be embedded on-chain during registration.
const { updateSessionPolicy, registerCurrentSession } = useCavos();

// ALWAYS update policy before registering
updateSessionPolicy({
  allowedContracts: [TOKEN_ADDRESS],
  spendingLimits: [{ token: TOKEN_ADDRESS, limit: BigInt(10 * 10**18) }],
  maxCallsPerTx: 5,
});
await registerCurrentSession();
[!WARNING] If you call registerCurrentSession() without updating the policy first, the stale policy from login time gets registered. If that policy was empty, policy_count == 0 on-chain — spending limits are skipped entirely.

renewSession()

Renew an expired session key. Requires the session to be registered on-chain and expired within the grace period (default: 48h after expiry).
try {
  const txHash = await renewSession();
  console.log('Session renewed:', txHash);
} catch (err) {
  if (err.message.includes('still active')) {
    // Session not expired yet
  } else if (err.message.includes('outside the grace period')) {
    // Must re-login
    await login('google');
  }
}

revokeSession(sessionKey)

Revoke a specific session key on-chain. Requires JWT verification.
const { revokeSession, sessionPublicKey } = useCavos();

// Revoke the current session key
if (sessionPublicKey) {
  await revokeSession(sessionPublicKey);
}

emergencyRevokeAllSessions()

Revoke all session keys by incrementing the on-chain revocation epoch. Use if you suspect a key was compromised.
if (confirm('Revoke all sessions? You will need to re-authenticate.')) {
  await emergencyRevokeAllSessions();
}
[!WARNING] This invalidates all sessions immediately. The user must re-login to transact again.

exportSession()

Export the current session as a base64-encoded token for use in the Cavos CLI or autonomous agents.
if (walletStatus.isSessionActive) {
  const token = exportSession();
  navigator.clipboard.writeText(token);
}
CLI Usage:
cavos session import <base64_token>
cavos transfer --to 0x... --amount 1.5 --token STRK
[!NOTE] The exported token contains the session private key. Store it securely and never share it publicly.

Account Methods

deployAccount()

Manually deploy the account contract. Normally called automatically after login().
const txHash = await deployAccount();

isAccountDeployed()

Check if the account contract is deployed on-chain.
const deployed = await isAccountDeployed();

getBalance()

Get the ETH balance of the wallet (as a string in wei).
const balance = await getBalance();

getAssociatedWallets()

Discover all wallets associated with the current user.
const wallets = await getAssociatedWallets();
// [{ address: '0x...', name: undefined }, { address: '0x...', name: 'trading' }]

switchWallet(name?)

Switch the active wallet by name (for multi-wallet users).
await switchWallet('trading'); // Switch to named sub-wallet
await switchWallet();          // Switch back to default wallet

SessionKeyPolicy Reference

interface SessionKeyPolicy {
  allowedContracts: string[];  // Only these contracts can be called
  spendingLimits: Array<{
    token: string;   // ERC-20 contract address
    limit: bigint;   // Max amount in token base units (MUST be bigint)
  }>;
  maxCallsPerTx: number;       // Max calls per multicall transaction
}
[!CAUTION] limit must be a bigint. Using number silently truncates large values.
// ✅ Correct
limit: BigInt(10 * 10**18)   // or BigInt('10000000000000000000')
// ❌ Wrong — number overflow
limit: 10 * 10**18

Complete Example

'use client';
import { useCavos } from '@cavos/react';

export default function App() {
  const {
    isAuthenticated, isLoading, address, walletStatus,
    login, logout, execute,
  } = useCavos();

  if (isLoading) return <p>Loading...</p>;

  if (!isAuthenticated) {
    return <button onClick={() => login('google')}>Login with Google</button>;
  }

  if (!walletStatus.isReady) {
    return (
      <div>
        {walletStatus.isDeploying && <p>Deploying wallet...</p>}
        {walletStatus.isRegistering && <p>Registering session...</p>}
      </div>
    );
  }

  const handleTransfer = async () => {
    const txHash = await execute({
      contractAddress: '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',
      entrypoint: 'transfer',
      calldata: ['0xrecipient...', '1000000000000000000', '0'],
    });
    console.log('tx:', txHash);
  };

  return (
    <div>
      <p>Wallet: {address}</p>
      <button onClick={handleTransfer}>Transfer 1 STRK</button>
      <button onClick={logout}>Logout</button>
    </div>
  );
}