LazorKit LogoLazorKit
SDK (web3.js v1)

Getting Started

Create a wallet, execute a transaction, look up a wallet by credential.

Ship the core flows with @lazorkit/sdk-legacy. Every client.* call returns TransactionInstruction[] — you wrap them into a Transaction, sign, and submit.

Vault PDA holds funds

createWallet returns vaultPda — the address where SOL and tokens live. Send/receive funds there. walletPda is the metadata PDA, used internally.

Prerequisites

  • Node.js 18+
  • @lazorkit/sdk-legacy and @solana/web3.js installed
npm install @lazorkit/sdk-legacy @solana/web3.js

1. Initialize the client

import { Connection } from '@solana/web3.js';
import { LazorKitClient } from '@lazorkit/sdk-legacy';

const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
const client = new LazorKitClient(connection);

2. Create a wallet

import { Keypair } from '@solana/web3.js';
import * as crypto from 'crypto';

const ownerKp = Keypair.generate();

const { instructions, walletPda, vaultPda, authorityPda } = await client.createWallet({
  payer: payer.publicKey,
  userSeed: crypto.randomBytes(32),
  owner: { type: 'ed25519', pubkey: ownerKp.publicKey },
});
import * as crypto from 'crypto';

const { instructions, walletPda, vaultPda, authorityPda } = await client.createWallet({
  payer: payer.publicKey,
  userSeed: crypto.randomBytes(32),
  owner: {
    type: 'secp256r1',
    credentialIdHash,    // 32-byte SHA256 of WebAuthn credential ID
    compressedPubkey,    // 33-byte compressed Secp256r1 public key
    rpId: 'your-app.com',
  },
});

See Smart Wallet concept for how passkey registration produces these fields.

Send the returned instructions as a single transaction. vaultPda is the address your users send funds to; walletPda is their metadata PDA anchor.


3. Execute a transaction

import { SystemProgram } from '@solana/web3.js';
import { ed25519 } from '@lazorkit/sdk-legacy';

const { instructions } = await client.execute({
  payer: payer.publicKey,
  walletPda,
  signer: ed25519(ownerKp.publicKey),
  instructions: [
    SystemProgram.transfer({
      fromPubkey: vaultPda,                 // funds live at the vault
      toPubkey: recipient,
      lamports: 1_000_000n,
    }),
  ],
});

ownerKp signs at transaction level alongside the fee_payer.

Passkey signing is two-phase — the authenticator can only sign after the challenge is computed:

// 1. Compute the challenge
const prepared = await client.prepareExecute({
  payer: payer.publicKey,
  walletPda,
  secp256r1: { credentialIdHash, publicKeyBytes: compressedPubkey, authorityPda },
  instructions: [
    SystemProgram.transfer({
      fromPubkey: vaultPda,
      toPubkey: recipient,
      lamports: 1_000_000n,
    }),
  ],
});

// 2. Authenticator signs the challenge
const webauthnResponse = await getWebAuthnResponse(
  prepared.challenge,
  'your-app.com',
  credentialId,
);

// 3. Build the final transaction
const { instructions } = client.finalizeExecute(prepared, webauthnResponse);

4. Look up a returning user

No need to store walletPda / authorityPda client-side — look them up by the credential hash the user authenticates with:

const [wallet] = await client.findWalletsByAuthority(credentialIdHash);
// {
//   walletPda,
//   authorityPda,
//   vaultPda,
//   role,              // 0=Owner, 1=Admin, 2=Spender
//   authorityType,     // 0=Ed25519, 1=Secp256r1
// }

Returns an array — a single credential can authorise multiple wallets. Most flows care about the first entry.


5. Transfer SOL (shorthand)

transferSol wraps execute with a SystemProgram.transfer from the vault:

import { ed25519 } from '@lazorkit/sdk-legacy';

const { instructions } = await client.transferSol({
  payer: payer.publicKey,
  walletPda,
  signer: ed25519(ownerKp.publicKey),
  recipient,
  lamports: 500_000_000n,
});

6. Program constants

import { PROGRAM_ID } from '@lazorkit/sdk-legacy';
// 4h3XoNReAgEcHVxcZ8sw2aufi9MTr7BbvYYjzjWDyDxS

Next steps