# Quickstart

Build a working headless flow in a few minutes: authenticate a user with email OTP, create an embedded Ethereum wallet, and sign a message. Every screen in this guide is your own React Native UI; the SDK only supplies the hooks.

## Prerequisites

Complete the [Installation](/get-started/frontend-sdks/react-native/installation) and [Setup](/get-started/frontend-sdks/react-native/setup) guides so `MoonXProvider` wraps your app.

## Step 1: Authenticate with email OTP

`useLoginWithEmail` exposes the two calls you need (`sendCode`, `loginWithCode`) plus a `state` machine to drive your UI:

```tsx
import { useState } from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import { useLoginWithEmail } from '@moon-x/react-native-sdk';

export function EmailLogin() {
  const [email, setEmail] = useState('');
  const [code, setCode] = useState('');
  const { state, sendCode, loginWithCode } = useLoginWithEmail();

  if (state.status === 'idle' || state.status === 'sending') {
    return (
      <View>
        <TextInput
          placeholder="you@example.com"
          autoCapitalize="none"
          keyboardType="email-address"
          value={email}
          onChangeText={setEmail}
        />
        <Button
          title={state.status === 'sending' ? 'Sending...' : 'Send code'}
          disabled={!email || state.status === 'sending'}
          onPress={() => sendCode({ email })}
        />
      </View>
    );
  }

  if (state.status === 'awaiting-code' || state.status === 'verifying') {
    return (
      <View>
        <Text>Enter the 6-digit code we sent to {state.status === 'awaiting-code' ? state.email : email}</Text>
        <TextInput
          placeholder="000000"
          keyboardType="number-pad"
          maxLength={6}
          value={code}
          onChangeText={setCode}
        />
        <Button
          title={state.status === 'verifying' ? 'Verifying...' : 'Log in'}
          disabled={code.length !== 6 || state.status === 'verifying'}
          onPress={() => loginWithCode({ code })}
        />
      </View>
    );
  }

  if (state.status === 'error') {
    return <Text>Login failed: {state.error.message}</Text>;
  }

  return <Text>Logged in!</Text>;
}
```

<Tip>
  MoonX also supports Google and Apple login on React Native. See [Authentication](/get-started/frontend-sdks/react-native/authentication#oauth-google-and-apple) for the OAuth flow.
</Tip>

## Step 2: Enroll a passkey

Wallet operations run a passkey presence ceremony, so the user must enroll a passkey before their first wallet can be created. Enrollment is not automatic; trigger it from a press handler:

```tsx
import { Button } from 'react-native';
import { usePasskeyStatus, useRegisterPasskey } from '@moon-x/react-native-sdk';

export function PasskeySetup() {
  const { hasPasskey } = usePasskeyStatus();
  const { registerPasskey, isRegistering } = useRegisterPasskey();

  if (hasPasskey) return null;

  return (
    <Button
      title={isRegistering ? 'Enrolling...' : 'Set up a passkey'}
      disabled={isRegistering}
      onPress={() => registerPasskey()}
    />
  );
}
```

Passkey enrollment requires the [platform association setup](/wallets/mobile-passkeys) (Associated Domains, asset links, and signing fingerprints) to be in place. See [Passkeys](/get-started/frontend-sdks/react-native/passkeys) for the full API.

## Step 3: Create an embedded wallet

Authenticated users start with no wallets. Mint one explicitly with `useCreateWallet`:

```tsx
import { Button } from 'react-native';
import { useCreateWallet } from '@moon-x/react-native-sdk';

export function CreateWalletButton() {
  const { createWallet } = useCreateWallet();

  const handleCreate = async () => {
    const { wallet } = await createWallet('ethereum');
    console.log('Wallet created:', wallet.public_address);
  };

  return <Button title="Create wallet" onPress={handleCreate} />;
}
```

Pass `'solana'` or `'tron'` to create wallets on those chains. List existing wallets with `useWallets('ethereum')`.

## Step 4: Sign a message

Chain-specific signing hooks live on subpath imports. Signing is headless: confirm intent with the user in your own UI first.

```tsx
import { Button } from 'react-native';
import { useWallets } from '@moon-x/react-native-sdk';
import { useSignMessage } from '@moon-x/react-native-sdk/ethereum';
import type { PublicWallet } from '@moon-x/react-native-sdk';

export function SignButton() {
  const { wallets } = useWallets('ethereum');
  const { signMessage } = useSignMessage();

  const handleSign = async () => {
    const wallet = wallets[0] as PublicWallet;
    if (!wallet) return;

    const { signature } = await signMessage({
      message: 'Hello from my app',
      wallet,
    });
    console.log('Signature:', signature);
  };

  return <Button title="Sign message" onPress={handleSign} />;
}
```

Sending transactions works the same way, with one addition: broadcast calls take an `rpcUrl` you supply per call. See [Signing](/get-started/frontend-sdks/react-native/signing) for the full EVM, Solana, and Tron surfaces.

## Step 5: Log out

```tsx
import { Button } from 'react-native';
import { useLogout } from '@moon-x/react-native-sdk';

export function LogoutButton() {
  const { logout } = useLogout();
  return <Button title="Log out" onPress={() => logout()} />;
}
```

`logout` clears the session from device storage and the secure context.

## Next steps

* [Authentication](/get-started/frontend-sdks/react-native/authentication) for OAuth, sessions, and tokens
* [Passkeys](/get-started/frontend-sdks/react-native/passkeys) to protect wallet key material
* [Wallets](/get-started/frontend-sdks/react-native/wallets) for import, export, and multi-wallet management
* [Signing](/get-started/frontend-sdks/react-native/signing) for the complete EVM, Solana, and Tron signing reference
