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-legacyand@solana/web3.jsinstalled
npm install @lazorkit/sdk-legacy @solana/web3.js1. 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