WDK logoWDK documentation

React Native Quickstart

Run the React Native wallet starter or integrate WDK into an existing app

The existing-app examples use @tetherto/wdk-react-native-core@1.0.0-beta.21. The starter path uses the dependency versions in its own repository lockfile.

What You'll Build

Use the starter to create or import a wallet, view balances and transfers, and explore optional cloud backup. Or integrate React Native Core into an existing app using the second path below.

The starter's configured networks and assets are listed on its overview. Network support in your own app depends on the wallet modules and providers you configure.

The starter is not configured entirely for testnets. Ethereum uses Sepolia, while Arbitrum and Polygon use mainnet. Before funding or sending, review .env and src/wdk/networks.ts. For Bitcoin, align btcNetwork with the Blockbook endpoint; setting a testnet endpoint alone does not change address derivation.

Prerequisites

Before we start, make sure you have:

ToolVersionWhy You Need It
Node.js22+To run JavaScript code
npm11.10.1+ (starter)To install starter dependencies
React Native0.83.6 (starter)Match your Expo SDK in existing apps
Android SDKAPI 29+Android minimum SDK version
iOS15.1+iOS deployment target

Quickstart Paths

Choose a starting point:

Run the starter with its existing wallet configuration and app screens.

→ Jump to Starter Template Setup


Option 1: Starter Template

The starter is an alpha app and may introduce breaking changes. It currently uses Expo SDK 55 and React Native 0.83.6. Use Node.js 22+ and npm 11.10.1+; Android builds also require JDK 17. Check its environment guide for native-toolchain setup.

Step 1: Clone the Starter

git clone https://github.com/tetherto/wdk-starter-react-native.git
cd wdk-starter-react-native

Step 2: Install Dependencies

npm install

The starter installs Cloud Backup 1.0.0-beta.1 and Indexer 1.0.1 from npm. Its postinstall script generates the WDK worklet bundle; keep the repository's dependency pins when following this path.

Step 3: Configure Environment

Copy the environment template for the network providers and optional integrations:

cp .env.example .env

To load the Activity tab, add a production WDK Indexer API key:

EXPO_PUBLIC_WDK_INDEXER_API_KEY=your_actual_api_key_here

The starter explicitly selects https://wdk-api.tether.io in src/wdk/indexer.ts; it does not read EXPO_PUBLIC_WDK_INDEXER_BASE_URL. Keep the explicit baseUrl if you modify the client setup.

Get your API key from the Indexer setup guide. The Indexer supplies transfer history. Balances and transfers use the network providers configured in .env.example, including EVM bundler and paymaster settings. TON and Tron are not wired into this starter.

Cloud backup is optional and needs separate Google/CloudKit configuration. Follow the starter's cloud backup setup before testing it. The starter overview explains its storage and backup roles.

Step 4: Run Your App

Generate native project files on the first run:

npx expo prebuild

Then build and launch the app:

npm run ios

After the native build completes, create or import a wallet. Balances and Activity require the integrations configured in Step 3.

→ Skip to What's Next


Option 2: Add to Existing App

Integrate WDK into your existing React Native or Expo project using @tetherto/wdk-react-native-core.

Step 1: Install

Core requires expo-crypto >=55.0.0 <57.0.0 and react-native-bare-kit >=0.14.5 as peer dependencies. Use an Expo SDK whose expo-crypto version satisfies that range. In a bare React Native app, install and configure Expo modules before installing expo-crypto.

Install Core and its peer dependencies:

npm install @tetherto/wdk-react-native-core@1.0.0-beta.21 react-native-bare-kit
npx expo install expo-crypto

Expo autolinking discovers transitive native dependencies by default in SDK 54 and later. If your app uses React Native Community CLI autolinking or Expo's legacy_shallowReactNativeLinking, also add Keychain to the app's dependencies so it can be linked:

npm install 'react-native-keychain@^10.0.0'

Rebuild the native app after installing dependencies, including iOS pods for bare React Native projects. Expo apps need a development build with the native modules included.

Step 2: Configure Android minSdkVersion

The library requires Android API 29 or higher to support react-native-bare-kit.

Add to your app.json or app.config.js:

{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "minSdkVersion": 29
          }
        }
      ]
    ]
  }
}

If you haven't installed expo-build-properties:

npx expo install expo-build-properties

Step 3: Configure the Bundle

The WDK engine runs inside a Bare worklet. Use the @tetherto/wdk-worklet-bundler CLI to generate an HRPC bundle with only the modules you need:

# 1. Install the bundler CLI
npm install -g @tetherto/wdk-worklet-bundler@1.0.0-beta.14

# 2. Initialize configuration in your React Native project
wdk-worklet-bundler init

# 3. Edit wdk.config.js to configure your networks (see example below)

# 4. Install required WDK modules (pick the ones you need)
npm install @tetherto/wdk @tetherto/wdk-wallet-evm-erc-4337

# 5. Generate the bundle
wdk-worklet-bundler generate

This generates a .wdk/ directory in your project. Import it:

import { bundle } from './.wdk'

Which WDK modules do I need? Each blockchain requires its own wallet module (e.g., wdk-wallet-evm-erc-4337 for Ethereum/Polygon, wdk-wallet-btc for Bitcoin). See the full list of available modules in the wdk-worklet-bundler documentation.

@tetherto/pear-wrk-wdk provides worklet transport and runtime handlers; it does not export a pre-built WDK bundle.

Step 4: Configure WDK Settings

Create a configuration file for your WDK setup (e.g., src/config/wdk.ts):

Set EXPO_PUBLIC_SEPOLIA_TEST_TOKEN_ADDRESS to a six-decimal USD₮ test token currently supported by your configured paymaster on Sepolia. Verify the address, decimals, and paymaster support through the provider's faucet and Sepolia deployment data. The address is public configuration. Use the same value in the wallet configuration and asset used for transfers.

The example uses Expo CLI environment-variable loading. For a bare React Native app, obtain the address through your existing public-config mechanism or replace the exported constant's value with the verified address; installing Expo modules alone does not load this environment variable.

// src/config/wdk.ts
import type { WdkConfigs } from '@tetherto/wdk-react-native-core'

export const sepoliaTestTokenAddress: string = process.env.EXPO_PUBLIC_SEPOLIA_TEST_TOKEN_ADDRESS ?? ''
if (!/^0x[0-9a-fA-F]{40}$/.test(sepoliaTestTokenAddress)) {
  throw new Error('Set EXPO_PUBLIC_SEPOLIA_TEST_TOKEN_ADDRESS to your verified test token')
}

export const wdkConfigs: WdkConfigs = {
  networks: {
    ethereum: {
      blockchain: 'ethereum',
      config: {
        chainId: 11155111, // Sepolia testnet
        safeModulesVersion: '0.3.0',
        provider: 'https://rpc.sepolia.org',
        bundlerUrl: 'https://api.candide.dev/public/v3/11155111',
        paymasterUrl: 'https://api.candide.dev/public/v3/11155111',
        paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
        transferMaxFee: 5000000,
        paymasterToken: {
          address: sepoliaTestTokenAddress,
        },
      },
    },
    // Add more networks as needed
  },
}

This example uses Sepolia testnet. Configure and fund the selected test token before sending. For production or mainnet configuration, see the Chain Configuration Guide.

Step 5: Add WdkAppProvider

Wrap your app with WdkAppProvider to enable wallet functionality throughout your app.

Add to your app/_layout.tsx:

// app/_layout.tsx
import { WdkAppProvider } from '@tetherto/wdk-react-native-core'
import { bundle } from '../.wdk'
import { Stack } from 'expo-router'
import { wdkConfigs } from '../src/config/wdk'

export default function RootLayout() {
  return (
    <WdkAppProvider bundle={{ bundle }} wdkConfigs={wdkConfigs}>
      <Stack />
    </WdkAppProvider>
  )
}

Step 6: Use Hooks

Now you can use the WDK hooks in any component inside WdkAppProvider:

Keep the selected wallet ID in your authenticated application state and pass it explicitly to Core. LOCKED.walletId is only an optional hint while a specific wallet is already being targeted.

import { useWdkApp, useWalletManager, useAccount } from '@tetherto/wdk-react-native-core'

function WalletScreen({
  walletId,
  authenticate,
}: {
  walletId: string
  authenticate: () => Promise<boolean>
}) {
  const { state } = useWdkApp()
  const { createWallet, unlock } = useWalletManager()
  const { address } = useAccount({ network: 'ethereum', accountIndex: 0 })

  const create = async () => {
    if (await authenticate()) await createWallet(walletId)
  }

  const open = async () => {
    if (await authenticate()) await unlock(state.walletId ?? walletId)
  }

  switch (state.status) {
    case 'INITIALIZING':
    case 'REINITIALIZING':
      return <Text>Loading...</Text>
    case 'NO_WALLET':
      return <Button title="Create Wallet" onPress={create} />
    case 'LOCKED':
      return <Button title="Unlock" onPress={open} />
    case 'READY':
      return <Text>Address: {address}</Text>
    case 'ERROR':
      return <Text>Error: {state.error.message}</Text>
  }
}

For the full list of available hooks and their parameters, see the React Native Core API Reference.

Step 7: Rebuild and Run

For Expo projects, run prebuild to apply native changes:

npx expo prebuild --clean
npx expo run:ios
# or
npx expo run:android

Congratulations! You've successfully integrated WDK into your React Native app!


What's Next?

Now that you have WDK integrated, here's what you can explore:

Send Transactions

Use the useAccount() hook to send transactions:

// src/screens/SendScreen.tsx
import { useAccount, BaseAsset } from '@tetherto/wdk-react-native-core'
import { sepoliaTestTokenAddress } from '../config/wdk'

const usdt = new BaseAsset({
  id: 'usdt-ethereum',
  network: 'ethereum',
  symbol: 'USDT',
  name: 'Tether USD',
  decimals: 6,
  isNative: false,
  address: sepoliaTestTokenAddress,
})

function SendScreen({ recipient }: { recipient: string }) {
  const { address, send } = useAccount({ network: 'ethereum', accountIndex: 0 })

  const handleSend = async () => {
    if (!address) return

    const result = await send({
      to: recipient,
      asset: usdt,
      amount: '1000000', // 1 USDt (6 decimals)
    })

    if (result.success) {
      console.log('TX hash:', result.hash, 'Fee:', result.fee)
    } else {
      console.error('TX failed:', result.error)
    }
  }

  return <Button title="Send 1 USDt" onPress={handleSend} />
}

This token configuration is for Sepolia. Validate the recipient for the configured chain and require the user to review the recipient, asset, amount, and fee before calling send().

Lock the Wallet

Use lock() to clear the active wallet identity and local address state:

function LockButton() {
  const { lock } = useWalletManager()

  const handleLock = async () => {
    // This does not stop the worklet or confirm disposal completion.
    await lock()
  }

  return <Button title="Lock" onPress={handleLock} />
}

When a wallet is active, lock() sends a one-way full WDK disposal request and then resets local lifecycle flags. The Bare worklet and IPC connection stay running, and the call does not wait for worklet cleanup acknowledgement. Your app still controls authentication and access; completing lock() does not confirm that every seed or key copy was erased from memory.

Refresh Balances

import { useRefreshBalance } from '@tetherto/wdk-react-native-core'

const { mutate: refreshBalance } = useRefreshBalance()

// Refresh all balances for account 0
refreshBalance({ accountIndex: 0, type: 'wallet' })

Troubleshooting

Android build fails with "Execution failed for task ':app:checkDebugAarMetadata'"

This means your minSdkVersion is too low. Make sure you've set it to 29:

{
  "expo": {
    "plugins": [
      ["expo-build-properties", { "android": { "minSdkVersion": 29 } }]
    ]
  }
}

Then rebuild:

npx expo prebuild --clean
npx expo run:android

"useWdkApp must be used within WdkAppProvider"

Ensure your component is rendered inside WdkAppProvider. The provider must be at the root of your component tree:

// Correct
<WdkAppProvider bundle={{ bundle }} wdkConfigs={configs}>
  <MyComponent />  {/* Can use useWdkApp() here */}
</WdkAppProvider>

// Wrong - hook used outside provider
<MyComponent />  {/* Cannot use useWdkApp() here */}
<WdkAppProvider bundle={{ bundle }} wdkConfigs={configs}>
  ...
</WdkAppProvider>

No biometric prompt appears before a wallet operation

Core does not prompt for biometrics or enforce another authentication policy. Authenticate in your app before wallet operations or key access.

Close app access immediately on backgrounding using your app's authentication state, and require authentication before restoring access on foreground. Calling lock() is optional lifecycle cleanup. If you also call it while a guarded operation is in progress, catch the rejection and retry after that operation settles. See the wallet lifecycle contract.

Worklet startup reports an error

Fix the reported bundle, configuration, or native startup cause, then restart the app before retrying.

Metro cache issues

If you see stale module errors after upgrading, clear the Metro cache:

npx expo start --clear
# or
npx react-native start --reset-cache

Complete Setup Checklist

For Expo projects:

  • Install @tetherto/wdk-react-native-core plus its expo-crypto and react-native-bare-kit peer dependencies
  • Configure Android minSdkVersion to 29 in app.json
  • Set up bundle (custom or pre-built)
  • Create WdkConfigs configuration
  • Add WdkAppProvider to app/_layout.tsx
  • Use hooks (useWdkApp, useWalletManager, useAccount, useBalance)
  • Run npx expo prebuild --clean before building

For bare React Native:

  • Install @tetherto/wdk-react-native-core plus its expo-crypto and react-native-bare-kit peer dependencies
  • Install and configure Expo modules before adding expo-crypto
  • Set minSdkVersion to 29 in android/build.gradle
  • Set up bundle (custom or pre-built)
  • Create WdkConfigs configuration
  • Wrap root component with WdkAppProvider
  • Rebuild native code

Learn More

Ready to dive deeper? Check out these resources:

Core Concepts

Examples & Starters

Need Help?

On this page