LazorKit LogoLazorKit
React SDK

useWallet

Hook API — connect, sign, manage sessions & authorities.

import { useWallet } from '@lazorkit/wallet';

Returns wallet state and every mutation method. WebAuthn runs inline through navigator.credentials — no redirect, no popup, no redirectUrl argument.

Quick reference

MethodPasskey promptUse for
connectyes (first time)Authenticate; silently restores on repeat visits
disconnectnoClear local state
signAndSendTransactionyesSingle-tx Execute via paymaster
signMessageyesOff-chain signature
verifyMessagenoBrowser-side signature verification via crypto.subtle
createSessionyesMint a scoped Ed25519 session (optional SpendingLimits)
signAndSendWithSessionnoSend using the active session keypair
revokeSessionyesClose the session, refund rent
addAuthorityyesGrant an Ed25519 admin / spender (keypair generated + stored)
signAndSendWithAuthoritynoSend signed by a stored Ed25519 authority
removeAuthorityyesRemove any authority by PDA
authorizeAndExecuteyes (×1)Deferred 2-tx flow for oversized payloads (atomic)
authorizeDeferredyesTX1 only — returns serialized payload for later TX2
executeDeferrednoTX2 only — submit from a previously-authorized payload

All send-tx methods share the same SendTxPayload shape and accept transactionOptions.txVersion: 'legacy' | 'v0' (default 'v0') so you can pick the wire format per call.


State

FieldTypeNotes
smartWalletPubkeyPublicKey | nullWallet PDA (internal authority). Not the fund account.
walletWalletInfo | nullFull persisted record. Use wallet.vaultPda for funds.
isConnectedboolean
isLoadingbooleanisConnecting || isSigning || ...
isConnectingboolean
isSigningboolean
errorError | null

smartWalletPubkey ≠ vault

smartWalletPubkey is the Wallet PDA the program uses to resolve authorities. The SOL-holding account is wallet.vaultPda — always use that as fromPubkey and as the address you display to users. If wallet.vaultPda is absent on an older cached record, derive it with findVaultPda(smartWalletPubkey).


Connection

connect

Authenticate the user. On first login this prompts the passkey; on repeat visits the portal silently re-attaches using the cached credential.

const wallet = await connect({ feeMode: 'paymaster' });
OptionTypeDefault
feeMode'paymaster' | 'user''paymaster'

Returns Promise<WalletInfo>.

disconnect

await disconnect();

Clears persisted session. The on-chain wallet is untouched.


Transactions

signAndSendTransaction

Single-transaction execution for payloads up to ~574 bytes of inner instructions.

const sig = await signAndSendTransaction({
  instructions: [ix],
  transactionOptions: {
    feeToken: 'USDC',                   // optional — pay fees in any SPL mint
    computeUnitLimit: 400_000,
    addressLookupTableAccounts: [alt],  // auto-switches wire format to v0
    clusterSimulation: 'devnet',
    txVersion: 'v0',                    // 'legacy' | 'v0' (default 'v0')
  },
});

Returns Promise<string> — confirmed signature.

legacy vs v0

The SDK defaults to v0 (matches the mobile SDK and supports ALT). Use txVersion: 'legacy' only if your downstream RPC/indexer requires the older wire format. ALT is v0-only — passing addressLookupTableAccounts with legacy throws.

authorizeAndExecute

Two-transaction deferred flow for payloads that don't fit in a single tx (Jupiter swaps with routes + ATAs, multi-CPI batches). One passkey prompt covers both txs.

const sig = await authorizeAndExecute({ instructions: jupiterSwapIxs });

How it works

TX1 Authorize records a SHA-256 hash of your instruction set. TX2 ExecuteDeferred replays the exact instructions — any mismatch is rejected. The SDK submits both and returns TX2's signature.

authorizeDeferred + executeDeferred

Same protocol mechanism as authorizeAndExecute, but split into two callable steps so you can defer or delegate TX2. authorizeDeferred returns a serialized payload that any party (server, bot, another device) can redeem later via executeDeferred — no passkey required for the second step.

// Step 1 — on the device with the passkey (requires user gesture):
const { signature: authSig, deferredPayload } = await authorizeDeferred({
  instructions: jupiterSwapIxs,
});

// Persist / transport — deferredPayload is a plain JSON string.
await saveToBackend(deferredPayload);

// Step 2 — can run later, anywhere, without the passkey:
const execSig = await executeDeferred({
  deferredPayload,
  transactionOptions: { txVersion: 'v0' },   // optional
});

When to prefer this

Use authorizeDeferred + executeDeferred when TX2 is driven by an external condition (cron job, order fill, deposit detected) rather than submitted immediately. For the happy path where both tx fire back-to-back, authorizeAndExecute is simpler.

Authorization has an on-chain expiry — if TX2 doesn't land in time, anyone can call reclaimDeferred (via LazorKitClient) to reclaim rent. See DeferredPayload.


Messages

signMessage

Off-chain passkey signature over an arbitrary string.

const { signature, signedPayload } = await signMessage('Sign in as user-123');

Both values are base64. Returns Promise<{ signature: string; signedPayload: string }>.

verifyMessage

Verify a signMessage result in-browser — no RPC call. Useful for server-side auth flows that want to validate before hitting the chain.

const ok = await verifyMessage({
  signedPayload,    // Uint8Array
  signature,        // Uint8Array
  publicKey,        // Uint8Array — 33-byte compressed secp256r1
});

Returns Promise<boolean>.


Session keys

createSession

Mint an Ed25519 session keypair. The SDK persists the private key internally so signAndSendWithSession can reuse it.

const { sessionPda, sessionPublicKey } = await createSession({
  expiresInSlots: 216_000n,             // ~24h; omit for SDK default
  spendingLimits: {
    solLifetimeCap: 5_000_000_000n,     // 5 SOL total
    solPerTxMax:    500_000_000n,       // 0.5 SOL per tx
    solRecurring:   {
      limit: 1_000_000_000n,            // 1 SOL per window
      windowSlots: 216_000n,            // 24h window
    },
  },
});
FieldTypeRequired
expiresInSlotsbigintno
spendingLimitsSpendingLimitsno

Limits are immutable

Session actions can't be changed once created. To adjust, revoke and recreate.

Returns Promise<{ sessionPda: string; sessionPublicKey: string }>.

See SpendingLimits for the full shape.

signAndSendWithSession

Send a transaction using the currently active session keypair. No passkey prompt.

const sig = await signAndSendWithSession({ instructions: [ix] });

Fails with 0xbd0 (ActionSolLimitExceeded) and related errors once limits are hit — see Troubleshooting.

revokeSession

await revokeSession();

Closes the active session PDA and refunds rent.


Ed25519 authorities

addAuthority

Adds a fresh Ed25519 authority on the smart wallet. The SDK generates the keypair internally and persists it for signAndSendWithAuthority.

import { ROLE_ADMIN, ROLE_SPENDER } from '@lazorkit/wallet';

const { authorityPda, authorityPublicKey } = await addAuthority({
  role: ROLE_SPENDER,                   // default if omitted
});

Returns Promise<{ authorityPda: string; authorityPublicKey: string }>.

signAndSendWithAuthority

Send a transaction signed by the stored Ed25519 authority. No passkey prompt.

const sig = await signAndSendWithAuthority({ instructions: [ix] });

removeAuthority

await removeAuthority(targetAuthorityPda);   // base58 string

You can't remove the last Owner. Transfer ownership first — use LazorKitClient directly if needed.


Lower-level escape hatch

Everything from the underlying TS SDK is re-exported:

import {
  LazorKitClient,
  findWalletPda, findVaultPda, findAuthorityPda,
  findSessionPda, findDeferredExecPda,
  Actions,
  ROLE_OWNER, ROLE_ADMIN, ROLE_SPENDER,
  AUTH_TYPE_ED25519, AUTH_TYPE_SECP256R1,
  PublicKey, Connection, Transaction, TransactionInstruction, Keypair,
} from '@lazorkit/wallet';

const client = new LazorKitClient(connection);
const list = await client.listAuthorities(walletPda);