This guide will help you migrate your Privy React SDK from v1.x.x to v2.0.0.

To install the latest version, install the package from the latest tag:

npm i @privy-io/react-auth@latest

New features and improvements 🎉

  • Removed ethers v5 dependency, allowing developers to more easily use ethers v6
  • Added support for submitting transactions without waiting for confirmation
  • Added UIs for Ethereum signTransaction

For the full set of changes check out our changelog.

Breaking changes

Authentication

  • Guaranteed that user.wallet is the first linked wallet on the user object. To maintain state of the latest connected wallet, interact with the wallets array directly.

  • Removed the forkSession method. This feature was experimental and has been removed.

  • Removed the PrivyProvider’s deprecated onSuccess prop - use the onSuccess callback registered via the useLogin hook instead.

Embedded wallets

  • Apps using custom auth providers must now explicitly configure wallet UIs in the dashboard, or use the updated showWalletUIs option.

  • Removed the PrivyProvider’s deprecated createPrivyWalletOnLogin prop. Use config.embeddedWallets.createOnLogin instead.

<PrivyProvider
  createPrivyWalletOnLogin={true} // Remove
  config={{
    embeddedWallets: {createOnLogin: 'users-without-wallets'} // Add
  }}
>
  ...
</PrivyProvider>
  • Removed the deprecated additionalChains and rpcConfig props from PrivyProvider config, please configure these via the supportedChains
<PrivyProvider
  config={{
    additionalChains: [], // Remove
    rpcConfig: {}, // Remove
    supportedChains: [] // Add
  }}
>
  ...
</PrivyProvider>
  • Removed the deprecated noPromptOnSignature configuration option. Configure wallet UIs in the dashboard, or use the updated showWalletUIs option.
<PrivyProvider
  config={{
    embeddedWallets: {
      noPromptOnSignature: true, // Remove
      showWalletUIs: false // Add
    }
  }}
>
  ...
</PrivyProvider>

EVM

  • Removed the deprecated getEthersProvider and getWeb3jsProvider from the ConnectedWallet class. Use getEthereumProvider instead.
const provider = await wallet.getEthersProvider(); // Remove
const privyProvider = await wallet.getEthereumProvider(); // Add
const provider = new ethers.providers.Web3Provider(privyProvider); // Add

const provider = await wallet.getWeb3jsProvider(); // Remove
const privyProvider = await wallet.getEthereumProvider(); // Add
const provider = new Web3(privyProvider); // Add
  • Ethereum sendTransaction method now returns a Promise<{hash: string}> instead of a Promise<TransactionReceipt>. To get the full details of the submitted transaction, use a library like viem.
const receipt = await sendTransaction({...}); // Remove
const {hash} = await sendTransaction({...}); // Add
const receipt = await publicClient.waitForTransactionReceipt({hash}); // Add
  • Removed the experimental waitForTransactionConfirmation config option as it is the default behavior.
<PrivyProvider
  config={{
    embeddedWallets: {
      waitForTransactionConfirmation: false // Remove
    }
  }}
>
  ...
</PrivyProvider>
  • Updated signMessage, signTypedData, sendTransaction, and signTransaction methods:
const {signMessage} = usePrivy();
// `uiOptions` and `address` are optional
const signature = await signMessage(message, uiOptions, address); // Remove
// the first argument should be formatted `{message: string}`
const {signature} = await signMessage({message}, {uiOptions, address}); // Add

Smart Wallets

  • Updated signMessage, signTypedData, and sendTransaction methods of the smart wallet client:
import {useSmartWallets} from '@privy-io/react-auth/smart-wallets';

const {client} = useSmartWallets();
// `uiOptions` and `address` are optional
const signature = await client.signMessage({message}, uiOptions, address); // Remove
const signature = await client.signMessage({message}, {uiOptions, address}); // Add

Solana

  • Migrated useSendSolanaTransaction from @privy-io/react-auth to useSendTransaction from @privy-io/react-auth/solana (Solana-specific export path)
import {useSendSolanaTransaction} from '@privy-io/react-auth'; // Remove
import {useSendTransaction} from '@privy-io/react-auth/solana'; // Add

...

const {sendSolanaTransaction} = useSendSolanaTransaction(); // Remove
const {sendTransaction} = useSendTransaction(); // Add
  • Removed sendSolanaTransaction from usePrivy in favor of exporting sendTransaction from useSendTransaction from @privy-io/react-auth/solana
import {usePrivy} from '@privy-io/react-auth'; // Remove
import {useSendTransaction} from '@privy-io/react-auth/solana'; // Add

...

const {sendSolanaTransaction} = usePrivy(); // Remove
const {sendTransaction} = useSendTransaction(); // Add
  • Removed delegateWalletAction from useSolanaWallets. Use delegateWallet from useDelegatedActions instead.
import {useSolanaWallets} from '@privy-io/react-auth/solana'; // Remove
import {useDelegatedActions} from '@privy-io/react-auth'; // Add

...

const {delegateWalletAction} = useSolanaWallets(); // Remove
delegateWalletAction(); // Remove

const {delegateWallet} = useDelegatedActions(); // Add
await delegateWallet({  // Add
  address: '<wallet to delegate>', // Add
  chainType: 'solana', // Add
}); // Add
  • Removed rpcUrl from fundWallet from useSolanaWallets. Set rpcUrl in config.solanaClusters prop of the PrivyProvider instead
import {useSolanaWallets} from '@privy-io/react-auth/solana';

const {fundWallet} = useSolanaWallets();
fundWallet({
  address: '<wallet to fund>',
  cluster: {name: 'mainnet-beta', rpcUrl: 'https://api.mainnet-beta.solana.com'}, // Remove
  cluster: {name: 'mainnet-beta'} // Add
});

<PrivyProvider
  appId="your-privy-app-id"
  config={{
    ...theRestOfYourConfig,
    // Replace this with your required clusters and custom RPC URLs
    solanaClusters: [{name: 'mainnet-beta', rpcUrl: 'https://api.mainnet-beta.solana.com'}] // Add
  }}
>
  {/* your app's content */}
</PrivyProvider>;

Connectors

  • Removed the setActiveWallet method - use the wallets array directly to interact with wallets.

Callbacks

  • Updated all non-error callbacks to use named arguments instead of positional arguments.
const {login} = useLogin({
  onComplete: (user, isNewUser, wasAlreadyAuthenticated, loginMethod, linkedAccount) => { // Remove
  onComplete: ({user, isNewUser, wasAlreadyAuthenticated, loginMethod, linkedAccount}) => { // Add

    console.log(user, isNewUser, wasAlreadyAuthenticated, loginMethod, linkedAccount);
    // Any logic you'd like to execute if the user is/becomes authenticated while this
    // component is mounted
  },
  ...
  onError: (error) => { // onError will continue to stay as a singular error argument
    console.log(error)
  }})

...
 const {reauthorize} = useOAuthTokens({
  onOAuthTokenGrant: (tokens: OAuthTokens, {user}: {user: User}) => {  // Remove
  onOAuthTokenGrant: ({tokens, user}) => {  // Add
    const oAuthToken = tokens.accessToken

  ...
  }})