Changelog
Protocol and SDK changes that affect integrators.
A running log of the changes that move user-visible SDK or on-chain behaviour. Internal refactors and perf work are summarised when they affect CU budgets or account sizes.
2026-04-22 — React Native SDK: deferred split, cross-device fix, RPC opt
@lazorkit/wallet-mobile-adapter@2.0.0-beta.2.
New
authorizeDeferred+executeDeferred+reclaimDeferredexposed onuseWallet. The 2-tx deferred flow can now run in pieces across devices / servers / scheduled jobs.authorizeAndExecutestays as the atomic one-call shortcut.authorizeDeferred(payload, options)— passkey-signed TX1. Returns{ signature, deferredPayload, deferredExecPda, counter }.executeDeferred({ deferredPayload, refundDestination?, transactionOptions? }, callbacks?)— no passkey. Submit TX2 from a prior payload (same app, another device, or a relayer).reclaimDeferred({ deferredExecPda, refundDestination? }, callbacks?)— no passkey. Close an expiredDeferredExecPDA and reclaim its rent.
DeferredPayload+ wire helpers re-exported.serializeDeferredPayload()/deserializeDeferredPayload()turn the in-memory payload into a JSON-safe string — unblocks cross-machine deferred flows.TxCallbacksinterface. Shared shape ({ onSuccess?, onFail? }) for flows that don't go through the passkey portal (noredirectUrlrequired).
Fixed
- Cross-device passkey recovery →
0x2 InvalidSignature. When a user signs in on a secondary device with an iCloud-synced / Google Password Manager passkey, the portal-reported compressed pubkey can drift from what the on-chain Authority stored — the secp256r1 precompile rejects the tx with0x2. Two-part fix:buildSecp256r1Paramsno longer forwards the cachedwallet.passkeyPubkey. The client reads the authoritative pubkey off-chain viareadAuthorityPubkey.saveWalletrefreshesWalletInfo.passkeyPubkeyfromreadAuthorityPubkeyon every recovery (whenfindWalletsByAuthorityreturns a match), so persisted records stay in sync with the Authority account.
Performance
- Single
getAccountInfofor counter + pubkey. NewreadAuthorityState(connection, authorityPda)helper decodes both fields in one round-trip.resolveSecp256r1now uses it when the caller doesn't pre-cachepublicKeyBytes— one fewer RPC per passkey-signed tx in the default path (and after the cross-device recovery fix, that default path is now the common path).
Note on WalletInfo convention
The RN SDK's WalletInfo keeps smartWallet pointing to the vault PDA (where
funds live) plus a separate walletPda field for the internal metadata account. The
React SDK's latest convention flipped this: smartWallet = internal wallet PDA,
vaultPda = vault. The two SDKs are therefore semantically different at the storage
layer — file an issue if
you need them aligned on your project.
2026-04-22 — React SDK: v0/legacy, deferred split, cross-device fixes
New
txVersion: 'legacy' | 'v0'per-call intransactionOptions(default'v0'). Every send-tx method —signAndSendTransaction,signAndSendWithSession,signAndSendWithAuthority,authorizeAndExecute,authorizeDeferred,executeDeferred— accepts it. ALT is v0-only; passingaddressLookupTableAccountswith legacy throws.authorizeDeferred+executeDeferredsplit the 2-tx flow.authorizeDeferredreturns a serializeddeferredPayloadstring that any party (server relayer, another device, a cron job) can later submit viaexecuteDeferredwithout the passkey.authorizeAndExecutestays as the atomic one-call shortcut.- Unified
SendTxPayload. All send-tx methods now share the same payload shape.
Fixed
- Cross-device passkey recovery (e.g. iCloud Keychain: wallet created on iPhone,
reused on Mac). The recovery branch now reads the on-chain compressed pubkey via
readAuthorityPubkeyon the authority account matched byfindWalletsByAuthority, instead of the previouslistAuthorities(walletPda).find(...)which could return a non-matching authority on wallets with multiple secp256r1 entries. smartWalletsemantics.smartWalletis the internal Wallet PDA; the SOL-holding account isvaultPda. An earlier branch wrotevaultPdainto thesmartWalletslot which then causedfindVaultto be applied twice, producing a bogus signer at tx-level and failing sig verification. Guide and examples updated to always surfacewallet.vaultPdato users.- Stale passkey pubkey cache. All passkey-signing actions
(
signAndSendTransaction,createSession,revokeSession,addAuthority,removeAuthority,authorizeAndExecute,authorizeDeferred) now sourcepublicKeyBytesfrom the on-chain authority (viaresolvePasskeyWallet→readAuthorityPubkey) instead of the cachedwallet.passkeyPubkey. Prevents custom program error 0x2 (InvalidSignature) from the secp256r1 precompile when the cache drifts across sessions or devices. - Paymaster request format. Requests now use object params
(
{ transaction, signer_key }) and includesigner_key = feePayer.toBase58()so multi-signer Kora pools always sign with the signer that matches the tx'sfeePayerposition. Fixes tx-level-32002 Transaction did not pass signature verificationseen with remote Kora deployments.
Developer experience
- Iframe signing re-enabled by default in
DialogManager; Safari/mobileconnectstill falls back to popup. Fixes the earlier "both branches opened a popup" bug in thesignpath. - Session / authority flows default to v0
VersionedTransaction+tx.sign([...])instead of legacypartialSign, aligning web SDK withwallet-mobile-adapter.
2026-04-20 — Phase 2.1 & API unification
Breaking
- Mode 0 dropped. The Secp256r1 auth path now only supports raw
clientDataJSONfrom a real browser authenticator.Secp256r1Signer.sign()must returnclientDataJson; thetypeAndFlagsparameter ofbuildAuthPayloadis gone. Bot / programmatic signing should use Ed25519 authorities instead. - Authority account layout changed. Secp256r1 authorities now store
rpIdHash(32 bytes) instead of the variable-lengthrpIdstring. Account size is fixed at 145 bytes (Ed25519 authorities stay at ~80 bytes). Instruction data forCreateWallet/AddAuthority/TransferOwnershipis unchanged — clients still sendrpId; the program hashes it once at write time. - Unified prepare/finalize API. Every Secp256r1 op now has a matching
client.prepare*/client.finalize*pair —prepareExecute/finalizeExecute,prepareCreateSession/finalizeCreateSession, etc. The old*Preparemethods that returned an embeddedfinalizeclosure are gone. - Fee model. Protocol fee is now charged on every transaction when the protocol
is enabled — not opt-in per
fee_payer. Registering aFeeRecordis opt-in for reward tracking; unregistered payers still pay the fee but don't accumulate a claim at token launch.
Improvements
- CU reductions. Execute Secp256r1:
10,883 → 9,495 CU(-12.8%). Execute Session:4,723 → 4,105 CU(-13.1%). Zero-copy compact instructions + elimination of Vec concats in the hot path. publicKeyBytesauto-fetch.Secp256r1Params.publicKeyBytesis now optional — the client reads the pubkey off the on-chain Authority account via the newreadAuthorityPubkeyhelper. Integrators only need to persistcredentialIdHashper user.- Serializable
DeferredPayload. NewserializeDeferredPayload()/deserializeDeferredPayload()helpers turn a payload into JSON-safe bytes. Unblocks cross-machine deferred flows (sign TX1 on the user's device, submit TX2 from a server relayer).
Security hardening
H1— vault / token invariants enforced around CPIs in session+actions execute (prevents round-trip swap bypass ofSolMaxPerTx).H2— admin paths require program-ownedProtocolConfigandTreasuryShardaccounts (prevents account-spoofing on withdraw / update).M1—clientDataJSONparser correctly skips strings nested inside objects.payerandadminAuthoritymetas switched to writable where required for rent refunds.
What to do
RN SDK upgrade guide
wallet-mobile-adapter ≥ 2.0.0 — no code changes for consumers, internal methods renamed.
React SDK upgrade guide
@lazorkit/wallet upgrades bring prepare/finalize + publicKeyBytes optional.
web3.js v1 types
Updated Secp256r1Params + cross-machine deferred helpers.
Troubleshooting
Full error code map kept in sync — including 0xbd0 session exhaustion.
Earlier milestones
- Solita artifacts removed. The SDK is now fully hand-written for fine-grained control over wire layout. No IDL-generated wrappers remain.
- Protocol fee system live on mainnet. Sharded treasury (16 shards by default),
per-
fee_payerFeeRecordcounters, fees collected in native SOL. - Seedless + session.money launched. First production integrators running on the LazorKit execution layer.
For the authoritative change history, browse the
lazorkit-protocol git log.