Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.phantom.com/llms.txt

Use this file to discover all available pages before exploring further.

After a user signs a message using the Phantom Connect SDK, verify the signature on your backend to prove they own the wallet.

Get a signature (frontend)

First, have the user sign a message using the SDK:
import { useSolana } from "@phantom/react-sdk";

function SignForVerification() {
  const { solana } = useSolana();

  const signAndVerify = async () => {
    const message = "Hello, please sign this message";
    
    // Sign the message using the SDK
    const { signature } = await solana.signMessage(message);

    // Send to your backend for verification
    const response = await fetch("/api/verify", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        address: await solana.getPublicKey(),
        message,
        signature,
      }),
    });

    const { verified } = await response.json();
    console.log("Signature verified:", verified);
  };

  return <button onClick={signAndVerify}>Sign and Verify</button>;
}

Verify a signature (backend)

Install dependencies

npm install @solana/web3.js tweetnacl bs58

Verify a Solana signature

import { PublicKey } from "@solana/web3.js";
import nacl from "tweetnacl";
import bs58 from "bs58";

function verifySignature(
  walletAddress: string,
  message: string,
  signature: string
): boolean {
  try {
    const publicKey = new PublicKey(walletAddress);
    const messageBytes = new TextEncoder().encode(message);
    const signatureBytes = bs58.decode(signature);

    return nacl.sign.detached.verify(
      messageBytes,
      signatureBytes,
      publicKey.toBytes()
    );
  } catch {
    return false;
  }
}

// Usage
const isValid = verifySignature(
  "7fUAJdStEuGbc3sM84cKRL6yYaaSstyLSU4ve5oovLS7",
  "Hello, please sign this message",
  "5eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."
);

console.log("Signature valid:", isValid);

API route example (Next.js)

// app/api/verify/route.ts
import { PublicKey } from "@solana/web3.js";
import nacl from "tweetnacl";
import bs58 from "bs58";
import { NextResponse } from "next/server";

export async function POST(request: Request) {
  const { address, message, signature } = await request.json();

  try {
    const publicKey = new PublicKey(address);
    const messageBytes = new TextEncoder().encode(message);
    const signatureBytes = bs58.decode(signature);

    const isValid = nacl.sign.detached.verify(
      messageBytes,
      signatureBytes,
      publicKey.toBytes()
    );

    if (!isValid) {
      return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
    }

    return NextResponse.json({ verified: true, address });
  } catch (error) {
    return NextResponse.json({ error: "Verification failed" }, { status: 400 });
  }
}