Cavos

Embedded Solana Wallet

A device-native, self-custodial embedded wallet for Solana. The signing key is created and used on the user's device — Cavos cannot see it, cannot sign with it, and cannot move funds. Available via @cavos/kit for web and @cavos/kit/react-native for mobile (Expo Development Builds, EAS, or bare React Native — Expo Go is not supported).

The spend key is derived from a MasterDEK that stays protected at rest by the device's non-extractable key (WebCrypto in the browser, the OS keystore on React Native). The SDK does not enforce non-extractability on Node or other server runtimes. Pass your own RPC on mainnet; without rpcUrl the kit falls back to public api.mainnet-beta.solana.com, which is rate-limited.

Evaluate a Solana embedded wallet

This page is for teams that need a Solana embedded wallet SDK — a native system account inside the product, not a Phantom connect button and not a PDA wrapper. If your users should never install a wallet app or handle a seed phrase, login selects the user, @cavos/kit resolves an Ed25519 address, and the device signs. Cavos cannot see that key, cannot sign with it, and cannot move funds.

Authentication is separate from signing. Hosted login is Google, Apple, or an enclave-verified 9-character email code. If you already authenticate users, pass a stable userId; Cavos login then turns off. Use an immutable primary key, not an email that can change. Email sign-in is not available on React Native yet. For production RPC, pass Helius, Triton, or QuickNode; the public mainnet endpoint is rate-limited.

How the Solana account signs

Cavos provisions a native Ed25519 system account — an ordinary Solana address, the same shape as Phantom, not a PDA and not a P-256 program account. There is no on-chain Cavos program. The spend key is HKDF of the MasterDEK ( cavos-ed25519-solana-v1, salted by appSalt). The same DEK derives the Stellar G… and the Starknet stark key in a multi-chain session. Changing appSalt derives a different address that will not match the registry row.

The user's Ed25519 key signs the Solana transaction message. Solana verifies that signature natively. A relayer can be named fee payer, but a co-signer cannot alter the instructions it signs. On the web the local wrap of the DEK and the spend key live in the IndexedDB of the Cavos vault, filed under your app. The first device mints a random MasterDEK, claims (userId, appId, chain, network) in the registry, and wins. A later device must unwrap that same DEK.

Native Solana does not use undeployed the way Starknet does. There is nothing to deploy: a system account appears when it first receives lamports. After connect, this device is ready once it holds the spend key. Connect does not airdrop SOL and there is no initialize instruction. execute(amount, destination) moves amount lamports (1 SOL = 1,000,000,000 lamports). executeInstructions submits the user's signed transaction so the account can own ATAs and call allowlisted programs. An App ID activates the relayer.

Who pays the fee

fee answers one question: who pays, and in what. 'self' is the default on Solana — the account pays SOL — because a system account is signer and fee payer at once. A fresh Starknet or Stellar account cannot deploy itself or meet the base reserve without help; that is why those adapters sponsor by default. fee: 'sponsored' names the Cavos relayer as fee payer when an App ID (or a relayer) is set. fee: { token } settles the fee in a token the account already holds, in the same transaction. No route puts SOL in the account; sponsorship covers the fee, never the balance.

The relayer only co-signs instructions whose program ids are in the app's Solana program allowlist (dashboard → Solana Programs), plus System, SPL Token, Token-2022, and Associated Token. A 1,000,000 CU cap also applies. sponsored: true still works as a deprecated alias for fee: 'sponsored'; fee wins if both are passed. The backend can transport, sponsor, or submit a transaction. It does not hold the user authority required to move funds.

Recovery on a new device

A Solana wallet has one spend key. A new device restores the MasterDEK; it does not add a second signer. After login, call enrollPasskeyDefault(). Signing up never prompts. The passkey stores an encrypted copy of the MasterDEK under its WebAuthn PRF. Anyone with that synced passkey can open the wallet. It is not 2FA. A reconnect that already has the local wrap reseals the enclave so a phone can recover even if the first enroll failed.

Hardware-isolated recovery is opt-in per environment. When it is enabled, sign in again with Google, Apple, or an enclave-verified email code and the Nitro enclave unwraps the MasterDEK onto this device. Email uses a 9-character code; it is not available on React Native yet. Recovery codes are not used on Solana. If every device and every restore factor is gone, Cavos cannot recover the wallet.

Install

Create an app in the Cavos dashboard and copy its App ID. Register callback URLs exactly. Never change appSalt after users exist. The React walkthrough lives in How to add an embedded wallet.

npm install @cavos/kit

Connect and execute

Use Cavos.connect with chains: ["solana"]. network: "testnet" resolves to solana-devnet; "mainnet" resolves to solana-mainnet. Narrow on wallet.chain === "solana" before chain-native calls. In React, wrap the tree in CavosProvider and call wallet.execute on the Solana handle rather than the context execute, which is Starknet-only.

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

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

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

const signature = await wallet.execute(
  1_000_000n, // 0.001 SOL in lamports
  recipientPublicKey,
  { fee: "sponsored" },
);

For UI apps, the same package exports the provider. Pass identity if you already authenticate users; Cavos login then turns off. Hosted login is Google, Apple, or sendOtp / verifyOtp.

import { CavosProvider } from "@cavos/kit/react";

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

What is shared, what is Solana-only

Login, registry lookup, the MasterDEK, passkey and enclave restore, and the kit packages are shared with the Starknet embedded wallet and the Stellar embedded wallet. One Cavos.connect can configure all three. Each chain still has its own address.

Solana-only: the Ed25519 system account, lamport execute, executeInstructions, three fee routes, the program allowlist, and the CU cap. There is no paymaster key. If you are still choosing a provider, the compare pages state when another product is the better fit. Pricing publishes the free 1,000 wallet creates and paid recovery options.

What you get

Self-custodial

Cavos never holds a key that can move user funds. The spend key is derived from a MasterDEK that only the user's devices can open.

Native account

A plain Ed25519 system account — no custom on-chain program and no PDA.

Fee routes

The account can pay SOL, the Cavos relayer can pay (fee: "sponsored"), or the fee can settle in a token the account holds.

Arbitrary programs

Beyond SOL transfers: SPL tokens, swaps, and other calls via executeInstructions (allowlisted programs, 1,000,000 CU cap).

Passkey and enclave restore

A synced passkey or an enclave sign-in restores the MasterDEK on a new device.

No deploy step

Connect unwraps the spend key. A system account appears when it first receives lamports. Connect does not airdrop SOL.

Frequently asked

What is a Solana embedded wallet?

A Solana embedded wallet is a native Ed25519 system account provisioned inside your application. The spend key is HKDF of a MasterDEK that stays on the device. The address is an ordinary Solana public key, the same shape as Phantom, not a PDA.

Is a Cavos Solana wallet a program-derived address?

No. Cavos provisions a native system account. The user key signs the Solana message. There is no on-chain Cavos program, no secp256r1 precompile account, and no extra on-chain signer to add.

How do fees work on a Solana embedded wallet?

Who pays is a per-call choice. Omitting fee (or fee: "self") makes the account pay SOL. fee: "sponsored" names the Cavos relayer as fee payer when an App ID is set. fee: { token } settles the fee in a token the account holds. A co-signer cannot alter the instructions it signs. Sponsorship of extra programs is allowlisted per app, with a 1,000,000 CU cap.

How do I add a Solana embedded wallet with @cavos/kit?

Install @cavos/kit, create an app in the Cavos dashboard, and pass the App ID into Cavos.connect or CavosProvider with chains: ["solana"]. Narrow on wallet.chain === "solana", then call execute(amount, destination) in lamports, or executeInstructions for allowlisted programs. Connect does not airdrop SOL.

How does recovery work on a Solana embedded wallet?

There is one spend key. A new device restores the MasterDEK with a passkey the user added (enrollPasskeyDefault after login) or with hardware-isolated recovery: sign in again with Google, Apple, or an enclave-verified email code when the enclave is enabled for the app. Recovery codes are not used on Solana.

What is Solana-specific versus shared across chains?

Shared: login, registry lookup, a MasterDEK, passkey and enclave restore, and the same kit packages. Solana-specific: the Ed25519 system account, lamport execute, executeInstructions, three fee routes, and the program allowlist. Native Solana has nothing to deploy; the account appears when it first receives lamports.

Start building on Solana.

Follow the quickstart to reach a sponsored transaction.

We use anonymous analytics to understand how developers use Cavos. No personal data is collected. Privacy Policy