Cavos

Quickstart

Install @cavos/kit, connect to one or more chains, and execute your first sponsored transaction.

The onboarding contract is shared across every Cavos adapter: authenticate, connect, then execute. Connect derives addresses and checks deployment status but never deploys. The first execute triggers deployment atomically with the user's operation.

1. Install

Terminal
npm install @cavos/kit

Create an app in the Cavos dashboard and copy its App ID. Starknet sponsorship also requires a paymaster API key. The current web SDK receives that value in browser code, so it must be treated as a client-visible, app-scoped sponsorship credential, never as an operator or treasury secret. Before production, bind and limit it by app, environment, origin, rate, and spend in the sponsorship service.

2. Connect

Connect derives the address for every configured chain and checks whether the account exists on-chain. It never deploys. Deployment happens lazily on the first execute call.

This Solana example needs only the public App ID in the browser:

TypeScript
import { Cavos } from "@cavos/kit";

const session = await Cavos.connect({
  chains: ["solana"],
  defaultChain: "solana",
  network: "testnet",
  appSalt: "my-app",
  identity: {
    userId: user.id, // stable, immutable identifier
    email: user.email,
  },
  appId: process.env.NEXT_PUBLIC_CAVOS_APP_ID,
});

console.log(session.address);  // the address for the default chain
console.log(session.status);   // "undeployed" | "ready" | "needs-device-approval"

The returned session is the default chain's wallet augmented with multi-chain methods. session.wallet("solana") returns the Solana wallet; session.chainStatus("solana") returns its status.

Status values

StatusMeaningCan execute?
undeployedAddress derived but no on-chain account yet.Yes — first execute deploys + runs atomically.
readyAccount deployed and this device is authorized.Yes
needs-device-approvalAccount deployed but this device is not a signer.No — route to device approval.

Lazy deploy: An undeployed wallet can sign messages (signMessage) and execute. The first execute call deploys the account and runs the user's operation in a single sponsored transaction. No separate deploy step is needed.

3. Execute

Narrow on wallet.chain so TypeScript exposes the correct native method. Both undeployed and ready wallets can execute.

TypeScript
const wallet = session.wallet("solana");

// Only needs-device-approval must be handled differently
if (wallet.status === "needs-device-approval") {
  throw new Error("This device must be approved first");
}

if (wallet.chain === "starknet") {
  const { transactionHash } = await wallet.execute(calls);
} else if (wallet.chain === "solana") {
  const signature = await wallet.execute(1_000_000n, solanaRecipient); // lamports
} else if (wallet.chain === "stellar") {
  const hash = await wallet.execute(1_000_000n, stellarRecipient); // stroops
}

When the account is undeployed, execute deploys it first, then runs the operation — atomically, in a single sponsored transaction. After the first execute, status becomes ready.

For arbitrary execution, Starknet accepts multicalls, Solana exposes executeInstructions, and Stellar exposes invokeContract. Each path has adapter-specific validation and sponsorship rules.

4. Configure the selected adapter

AdapterRequired configurationSponsored execution
StarknetApp ID, network, app salt, identity, paymasterApiKeyPaymaster sponsors deploy and execute.
SolanaApp ID, network, app salt, identityHosted relayer becomes fee payer; arbitrary programs are allowlisted per app.
StellarApp ID, network, app salt, identityHosted relayer can sponsor reserves and fee-bump submission.

Use the dedicated Starknet, Solana, or Stellar guide for production configuration.

5. Handle device approval

status === "needs-device-approval" means the identity already owns a wallet but this device is not enrolled. Do not create a second wallet. Route the user through Passkeys (recommended), Multi-device, or the adapter's Recovery path.

Passkeys and recovery share a product goal across chains, but their cryptographic implementation differs. Use the high-level wallet methods and follow the selected adapter's capability notes.

More than one chain

Connect once, access every configured chain. The session provides wallets for all chains without re-authenticating:

TypeScript
const session = await Cavos.connect({
  chains: ["solana", "stellar", "starknet"],
  defaultChain: "solana",
  network: "testnet",
  appSalt: "my-app",
  identity,
  appId: process.env.NEXT_PUBLIC_CAVOS_APP_ID,
  paymasterApiKey: process.env.NEXT_PUBLIC_CAVOS_PAYMASTER_API_KEY, // Starknet only
});

// Access any chain's wallet
const solana = session.wallet("solana");
const stellar = session.wallet("stellar");
const starknet = session.wallet("starknet");

// Check status per chain
console.log(session.chainStatus("solana"));   // "undeployed" | "ready" | "needs-device-approval"
console.log(session.chainStatus("stellar"));
console.log(session.chainAddress("stellar")); // G… address

// Execute on Stellar (works even if undeployed — first execute creates the account)
if (stellar.chain === "stellar" && stellar.status !== "needs-device-approval") {
  await stellar.execute(10_000_000n, "GDEST...");
}

Each chain has its own address, derived from the same identity. Deploy happens independently per chain on first execute. One login, multiple wallets, no re-authentication.

React

For UI applications, use the bindings from @cavos/kit/react:

app/providers.tsx
import { CavosProvider } from "@cavos/kit/react";

export function Providers({ children }) {
  return (
    <CavosProvider
      config={{
        chains: ["solana", "stellar"],
        defaultChain: "solana",
        network: "testnet",
        appSalt: "my-app",
        appId: process.env.NEXT_PUBLIC_CAVOS_APP_ID,
      }}
      modal={{ appName: "My App" }}
    >
      {children}
    </CavosProvider>
  );
}

The provider gives you setChain(chain) to switch the active chain without re-mounting or re-authenticating. See React for the full guide.

Continue with React, React Native, or the full API reference.

If something does not behave as described here, Troubleshooting lists the symptoms integrators hit most and what actually causes them.

On this page