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
| Method | Passkey prompt | Use for |
|---|---|---|
connect | yes (first time) | Authenticate; silently restores on repeat visits |
disconnect | no | Clear local state |
signAndSendTransaction | yes | Single-tx Execute via paymaster |
signMessage | yes | Off-chain signature |
verifyMessage | no | Browser-side signature verification via crypto.subtle |
createSession | yes | Mint a scoped Ed25519 session (optional SpendingLimits) |
signAndSendWithSession | no | Send using the active session keypair |
revokeSession | yes | Close the session, refund rent |
addAuthority | yes | Grant an Ed25519 admin / spender (keypair generated + stored) |
signAndSendWithAuthority | no | Send signed by a stored Ed25519 authority |
removeAuthority | yes | Remove any authority by PDA |
authorizeAndExecute | yes (×1) | Deferred 2-tx flow for oversized payloads (atomic) |
authorizeDeferred | yes | TX1 only — returns serialized payload for later TX2 |
executeDeferred | no | TX2 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
| Field | Type | Notes |
|---|---|---|
smartWalletPubkey | PublicKey | null | Wallet PDA (internal authority). Not the fund account. |
wallet | WalletInfo | null | Full persisted record. Use wallet.vaultPda for funds. |
isConnected | boolean | |
isLoading | boolean | isConnecting || isSigning || ... |
isConnecting | boolean | |
isSigning | boolean | |
error | Error | 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' });| Option | Type | Default |
|---|---|---|
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
},
},
});| Field | Type | Required |
|---|---|---|
expiresInSlots | bigint | no |
spendingLimits | SpendingLimits | no |
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 stringYou 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);