LazorKit LogoLazorKit
Wallet Standard

Wallet Standard

Integration with Solana Wallet Adapter and other frameworks.

Wallet Standard Support

The LazorKit SDK supports the Solana Wallet Standard, enabling compatibility with popular libraries like @solana/wallet-adapter-react.

Integration with @solana/wallet-adapter-react

Default Configuration

// Standard Devnet Configuration

export const DEFAULT_CONFIG = {
  rpcUrl: 'https://api.devnet.solana.com',
  portalUrl: 'https://portal.lazor.sh',
  paymasterConfig: {
    paymasterUrl: 'https://kora.devnet.lazorkit.com',
    // apiKey: 'YOUR_API_KEY' // Optional
  },
  clusterSimulation: 'devnet'
};

You can use LazorKit seamlessly alongside other standard wallets (like Phantom, Solflare) in your existing provider setup.

1. Installation

Install the necessary dependencies:

npm install @lazorkit/wallet @solana/wallet-adapter-react @solana/wallet-adapter-react-ui

2. Register the Wallet

Call registerLazorkitWallet with your config. This adds LazorKit to the list of wallets so your adapter can find it.

Use useEffect to ensure registration happens only on the client-side, as the Wallet Standard relies on the global window object.

For Create React App / Vite (SPA)

You can call this at the top level of your entry file if you don't need runtime configuration.

3. Setup Provider

Here is an example setup using ConnectionProvider and WalletProvider:

import { useMemo, useEffect } from 'react';
import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
import { WalletModalProvider } from '@solana/wallet-adapter-react-ui';
import { registerLazorkitWallet } from '@lazorkit/wallet';

// Configuration
const CONFIG = {
  RPC_URL: "https://api.devnet.solana.com",
  PORTAL_URL: "https://portal.lazor.sh",
  PAYMASTER: {
    paymasterUrl: "https://kora.devnet.lazorkit.com",
    apiKey: "YOUR_API_KEY" 
  },
  CLUSTER: 'devnet'
};

export function AppProviders({ children }) {
  // Register LazorKit once on mount
  useEffect(() => {
    registerLazorkitWallet({
        rpcUrl: CONFIG.RPC_URL,
        portalUrl: CONFIG.PORTAL_URL,
        paymasterConfig: CONFIG.PAYMASTER,
        clusterSimulation: CONFIG.CLUSTER,
    });
  }, []);

  const wallets = useMemo(
    () => [
      // Standard wallets are automatically detected. 
      // LazorKit will appear here after registration.
    ],
    []
  );

  return (
    <ConnectionProvider endpoint={CONFIG.RPC_URL}>
      <WalletProvider wallets={wallets} autoConnect>
        <WalletModalProvider>
          {children}
        </WalletModalProvider>
      </WalletProvider>
    </ConnectionProvider>
  );
}

Usage in Components

Once connected, you can use the standard useWallet hook from @solana/wallet-adapter-react:

import { useWallet } from '@solana/wallet-adapter-react';
import { SystemProgram, PublicKey } from '@solana/web3.js';

export function TransferButton() {
  const { publicKey, sendTransaction } = useWallet();

  const handleSend = async () => {
    if (!publicKey) return;

    const transaction = new Transaction().add(
      SystemProgram.transfer({
        fromPubkey: publicKey,
        toPubkey: new PublicKey("RECIPIENT"),
        lamports: 1000,
      })
    );

    // LazorKit abstracts the Paymaster logic internally here
    const signature = await sendTransaction(transaction, connection);
    console.log("Sig:", signature);
  };

  return <button onClick={handleSend}>Send SOL</button>;
}

Message Signing & Verification (P256)

WebAuthn keys use the P256 curve (secp256r1), which works differently than standard Solana Ed25519 keys. The signMessage function returns a JSON object containing two critical pieces of data:

  1. signature: The signature produced by the WebAuthn authenticator.
  2. signedPayload: The exact byte sequence that was signed by the authenticator, encoded as base64

Why two values?

In WebAuthn, the authenticator does not sign the challenge directly. Instead, the browser constructs clientDataJSON (which includes the challenge, origin, and operation type), and the authenticator signs a payload derived from both:

  • Browser data (clientDataJSON)

  • Authenticator data (authenticatorData)

Because this signed payload cannot be reconstructed from the challenge alone, the verifier must know the exact bytes that were signed in order to validate the signature.

For this reason, signedPayload is returned alongside the signature.

Here's how to decode and use them:

import { useWallet } from '@solana/wallet-adapter-react';

export function SignAndVerify() {
  const { signMessage } = useWallet();

  const handleSign = async () => {
    if (!signMessage) return;

    const message = new TextEncoder().encode("Please verify ownership");
    
    // 1. Sign the message
    const result = await signMessage(message);

    // 2. Decode the result
    const jsonString = new TextDecoder().decode(result);
    const { signature, signedPayload } = JSON.parse(jsonString);

    console.log("Signature:", signature);
    console.log("Original Callenge:", signedPayload);

    // 3. Verify (Conceptual)
    // verify(publicKey, signedPayload, signature);
  };

  return <button onClick={handleSign}>Sign Message</button>;
}

How It Works

  1. Register: The register function sets up the LazorKit adapter.
  2. Discover: Your wallet provider sees the new adapter and adds it to the list.
  3. Transact: When you send a transaction, LazorKit handles the gas sponsorship (Paymaster) and signing logic automatically.

Note: LazorKit behaves like any other wallet to your app, but uses smart contracts and passkeys under the hood.