Skip to content

Epic: Phantom Connect embedded wallets — email-first onboarding, no wallet install #983

Description

@Potolski

Learners who arrive without a Solana wallet currently hit a hard wall: install an extension, save a seed phrase, fund it. For a platform whose whole point is teaching people Solana, that barrier sits before the first lesson.

Phantom Connect removes it. A learner signs in with Google or Apple and receives a real Phantom embedded wallet — no extension, no seed phrase, no SOL. Later, when they download Phantom and sign in with the same Google account, the same wallet is there.

Why this integration is small for us

Our program only requires a learner signature on one instruction:

Instruction Signer
enroll learner (signer + payer)
complete_lesson backend_signer only
finalize_course backend_signer only
issue_credential payer + backend_signer + asset keypair — learner is a referenced account, not a signer

Every XP award and credential mint already works from the learner's pubkey alone. We need an address bound to an email identity, plus a signature on enroll.

/api/auth/link-wallet already verifies SIWS, rejects cross-account and conflicting links, and mints accrued XP on link. An embedded wallet plugs into that route unchanged.

Key facts (verified against Phantom's docs)

  • Free. "There's no cost to use Phantom Connect. Phantom provides authentication, embedded wallets, and signing infrastructure at no charge to developers."
  • Non-custodial. Keys never pass through our app or backend and never appear in plaintext. We never become a custodian.
  • Wallet continuity is real. Embedded wallets are tied to the user's Google/Apple identity in Phantom's own account namespace — the docs describe an account picker over "any Phantom accounts tied to their Google or Apple identity." Downloading Phantom and choosing Continue with Google surfaces the same wallet. No export/import step.
  • Extension users share one code path. If a user connects the Phantom extension, no embedded wallet is created and the app receives the account through the identical SDK interface.
  • Constraints: $1,000/day per-app spending limit (irrelevant — we move no value), 7-day session, and users can revoke via Settings → Connected Apps.

What Phantom does NOT do

It does not accept our Supabase JWT to provision a wallet. The "Wallet Authentication with JWTs" guide is the opposite direction — the wallet signs, our backend verifies and issues a JWT. That is SIWS, which we already have.

So Phantom owns the wallet login and Supabase stays the account identity, bridged by /api/auth/link-wallet. Learners see two Google prompts (usually one tap for the second, since the browser already holds a Google session). Collapsing to a single login is possible later by making Phantom primary and minting the Supabase session from the SIWS verification — out of scope here.

Blocker worth knowing up front

@phantom/react-sdk@2.0.2 declares peerDependencies: { react: ">=19.0.1" }. We are on React 18.3.1 with Next 15.5.22. The React SDK cannot be installed without a React 19 upgrade across ~165 components.

@phantom/browser-sdk@2.0.2 has no React peer dependency and is the framework-agnostic entry point. We use that and wrap it in our own hook. See the React 19 issue for whether to revisit.

Sub-issues

Migration note

profiles.wallet_address is a single TEXT UNIQUE column, and link-wallet treats links as permanent — a learner with a different wallet already linked gets a 409. Existing learners split into two groups:

  • No wallet linked — embedded wallet fills the empty slot cleanly.
  • External wallet already linked — the slot is taken and their XP lives at that address. They keep using it; embedded provisioning must be gated on wallet_address IS NULL.

Size the split before designing anything further:

select count(*) filter (where wallet_address is not null) as has_wallet,
       count(*) filter (where wallet_address is null)     as no_wallet
from profiles where deleted_at is null;

Docs: Phantom Connect · SDK Overview · Wallet Auth with JWTs

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:authLaunch-experience workstreamarea:frontendNext.js frontendenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions