LazorKit LogoLazorKit
React Native SDK

Upgrading to 2.x

What changes when you move @lazorkit/wallet-mobile-adapter from 1.x or the 2.0.0 betas to 2.x, release by release.

@lazorkit/wallet-mobile-adapter 2.0.0 is the release that speaks protocol v2. 2.3.0 is the current release. Full notes: the package CHANGELOG and the Changelog here.

npm install @lazorkit/wallet-mobile-adapter@^2.3.0

Do not install @beta

The npm beta tag still points at 2.0.0-beta.4, a pre-v2 build from April 2026. Use latest (2.3.0).

The adapter depends on expo-web-browser ^14.2.0 || ^15.0.0 (Expo SDK 53 / 54) and expo-crypto ^15.0.8 (Expo SDK 54); it is built and tested against Expo SDK 54. Older Expo SDKs have not been tested.

Checklist

  1. smartWalletPubkey is the vault (since the April 2026 betas; 1.x stored the wallet PDA there). Persisted wallets are migrated on load. Use smartWalletPubkey (alias vaultPubkey) as the user's address; walletPdaPubkey is the internal wallet PDA. Do not derive addresses with findVaultPda: the PDA helpers derive v2 addresses at the mainnet id.
  2. Sessions need limits. createSession without actions throws unless you pass unrestricted: true.
  3. Authorities need a role, and Delegates a policy. addAuthorityEd25519 has no default role (since 2.3.0: see below). role: ROLE_SPENDER (Delegate) on a v2 wallet throws without policy: serializeActions([...]). On a v1 wallet it requires unrestricted: true and refuses a policy.
  4. v1 users need their relayer: set v1ConfigPaymaster when configPaymaster points at a v2 relayer. See Migrating from v1.
  5. RPC URLs that do not name a cluster need the cluster prop.
  6. connect may ask the user, with a chooser drawn as a React Native Modal. On iOS it cannot appear over another modal: see Wallet Confirmation › iOS. While a wallet is connected, connect returns it without opening the portal; disconnect first to connect another passkey.
  7. Sends resolve later and can reject in new ways (2.1.0). See Sending transactions.
  8. Callbacks run after isSigning clears (2.2.0; connect and disconnect too since 2.3.0), and the store's signAndExecuteTransaction, signMessage and transferSol resolve with their results.
  9. Removed or changed exports: the low-level create*Ix builders are gone (use LazorKitClient methods). readAuthorityState is still exported for compatibility; new code should prepare passkey challenges with @lazorkit/sdk-legacy's prepare* methods and their minContextSlot / commitment options (Passkey signing).
  10. Android redirects. More than one app can claim a custom URL scheme on Android. Prefer a redirect only your app can receive, such as a verified App Link; see Wallet Confirmation.

Release by release

ReleaseWhat changes for you
2.0.0 (2026-09-29)Protocol v2 beside v1, routed per wallet: protocolVersion, v1ConfigPaymaster, cluster, V1WalletRetiredError, V1WalletMigratedError. Wallet confirmation: onConfirmWallet, confirmWallet, trustedAuthorities, watchMints, <WalletChooser />, WalletChooserNotShownError, PortalCancelledError. A signing action always rejects on failure (it could resolve undefined). The breaking items above.
2.1.0 (2026-09-30)Sends resolve once confirmed (polling, no websocket); new errors TransactionFailedError, TransactionExpiredError, TransactionOutcomeUnknownError, ConfirmationTimeoutError, PreviousTransactionPendingError, SignatureReusedError, PaymasterError. Passkey transactions are sequenced. A passkey with no wallet can connect (key recovered from two signatures, one extra prompt). Large previews use the lookup tables instead of failing before the portal. expo-web-browser 15 accepted.
2.2.0 (2026-09-30)Callbacks run after isSigning clears; a send from onSuccess runs. Deferred window: expiryOffset 10–9000, default 1500 (was 300). DeferredExpiredError and its helpers.
2.2.1 (2026-10-01)An inner program's 3014 is no longer reported as DeferredExpiredError; isDeferredExpiredError is true for every DeferredExpiredError; DeferredFailureContext.
2.3.0 (2026-10-02)Breaking: addAuthorityEd25519 needs a role. connect and disconnect follow the callback contract, on the hook and the store; transferSol with no wallet calls onFail. The is*Error predicates recognise the adapter's own errors through wrappers. See below.

Keys

The adapter never stores session or authority keys: createSession and addAuthorityEd25519 take a public key, and you keep the secret key. Store it in the platform's secure storage (for example expo-secure-store), not in AsyncStorage. What the adapter keeps in AsyncStorage: the wallet record and config (lazor-wallet-store), and per passkey the slot of its last transaction and any send whose outcome is unknown (lazorkit:passkey-lane:…, the slot for ten minutes).

Keep the wallet a session key belongs to with the key, use the key only while that wallet is connected, and delete it at disconnect and once its session is revoked or expired. The package README's Session keys section (2.3.0) shows this with expo-secure-store.

Upgrading to 2.3.0

From 2.2.x. Full notes: the package CHANGELOG.

  1. addAuthorityEd25519 needs a role (breaking). There is no default any more. A call without role does not compile, and at runtime a missing role, or one that is not ROLE_OWNER, ROLE_ADMIN or ROLE_SPENDER, throws before the portal opens. So does ROLE_OWNER on a v2 wallet, where the adapter never adds an Owner. Migration: addAuthorityEd25519({ newEd25519Pubkey, role: ROLE_SPENDER, policy }, options) keeps 2.2.1's behaviour. Ranks: useWallet › addAuthorityEd25519.
  2. connect and disconnect callbacks follow the sign actions' contract: they run once the call is over, and a throwing onSuccess is logged and changes nothing (in 2.2.1 it made the call reject). The store's connect calls onSuccess / onFail (2.2.1 ignored them), and its disconnect takes them. A second connect while one runs calls onFail at once.
  3. transferSol with no wallet connected calls onFail and sets error, as every other action does (2.2.1 rejected without either).
  4. Predicates. isSignatureReusedError, isRetiredDeploymentError and isDeferredExpiredError recognise the adapter's own errors, through cause and a wallet-adapter WalletError, so they work where instanceof does not.