> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crossmint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin

> Create user wallets from your mobile app in under 5 minutes

<Note>
  **This page has been updated for Wallets SDK V1.** If you are using the previous version,
  see the [previous version of this page](/wallets/v0/quickstarts/kotlin) or the [V1 migration guide](/wallets/guides/migrate-to-v1).
</Note>

This quickstart integrates the Crossmint Kotlin SDK into an Android app to create a non-custodial wallet for a user, fund it with testnet USDC on Base Sepolia, and send USDC to another wallet.

<CardGroup cols={2}>
  <Snippet file="before-you-start.mdx" />

  <Card title="Android Demo App" icon="github" iconType="duotone" href="https://github.com/Crossmint/android-quickstart">
    See a full working example with a repository to clone.
  </Card>
</CardGroup>

<Steps>
  <Step title="Install the SDK">
    Add the Crossmint dependencies to your app's `build.gradle.kts` file:

    <Tabs>
      <Tab title="Version Catalog">
        ```kotlin build.gradle.kts theme={null}
        dependencies {
            implementation(libs.crossmint.sdk)
            implementation(libs.crossmint.compose)
        }
        ```

        ```toml libs.versions.toml theme={null}
        [versions]
        crossmint-sdk = "0.0.16"

        [libraries]
        crossmint-compose = { module = "com.crossmint:crossmint-compose", version.ref = "crossmint-sdk" }
        crossmint-sdk = { module = "com.crossmint:crossmint-sdk", version.ref = "crossmint-sdk" }
        ```
      </Tab>

      <Tab title="Direct">
        ```kotlin build.gradle.kts theme={null}
        dependencies {
            implementation("com.crossmint:crossmint-sdk:0.0.16")
            implementation("com.crossmint:crossmint-compose:0.0.16")
        }
        ```
      </Tab>
    </Tabs>

    ### Requirements

    * Minimum Android SDK: API 24 (Android 7.0+)
    * JDK 11 or newer
    * Jetpack Compose
  </Step>

  <Step title="Configure your API key">
    The Crossmint SDK requires a client API key for authentication. <br />
    [Get your client API key](/introduction/platform/api-keys/client-side) using the Crossmint Console.
  </Step>

  <Step title="Initialize the Crossmint SDK">
    Initialize the SDK in your `Application` class, then wrap your composable tree with `CrossmintNonCustodialSignerProvider` to enable the non-custodial signing layer. This example uses [Crossmint Auth](/authentication/introduction) but you can use [any authentication provider of your choice](/wallets/guides/bring-your-own-auth).

    ```kotlin MyApplication.kt theme={null}
    import android.app.Application
    import com.crossmint.kotlin.Crossmint

    val CROSSMINT_API_KEY = "ck_staging_your_key_here"

    class MyApplication : Application() {
        override fun onCreate() {
            super.onCreate()
            Crossmint.shared(apiKey = CROSSMINT_API_KEY, appContext = this)
        }
    }
    ```

    Then wrap your top-level composable with `CrossmintNonCustodialSignerProvider`:

    ```kotlin theme={null}
    @Composable
    fun QuickstartApp() {
        CrossmintNonCustodialSignerProvider(sdk = Crossmint.instance) {
            // Your app content goes here
            AppContent()
        }
    }
    ```

    <Note>
      The environment (staging vs production) is automatically determined by your API key. Staging keys start with `ck_staging_`, production keys with `ck_production_`.
    </Note>

    ### Access SDK instances

    Inside the `CrossmintNonCustodialSignerProvider`, access SDK functionality using `LocalCrossmintSDK.current`:

    ```kotlin theme={null}
    import com.crossmint.kotlin.auth.CrossmintAuthManager
    import com.crossmint.kotlin.compose.LocalCrossmintSDK

    val sdk = LocalCrossmintSDK.current
    val authManager = sdk.authManager as CrossmintAuthManager
    val wallets = sdk.crossmintWallets
    ```
  </Step>

  <Step title="Authenticate users">
    Actions within the SDK are user-based, so first you need to authenticate a user.
    Choose your authentication method:

    <Tabs>
      <Tab title="Crossmint Auth (OTP)">
        Use Crossmint's built-in OTP authentication for easy, email-based login.

        <Steps>
          <Step title="Send OTP to user's email">
            ```kotlin theme={null}
            import com.crossmint.kotlin.auth.CrossmintAuthManager
            import com.crossmint.kotlin.compose.LocalCrossmintSDK

            val authManager = LocalCrossmintSDK.current.authManager as CrossmintAuthManager
            val email = "user@example.com"

            when (val result = authManager.sendOtp(email)) {
                is Result.Success -> {
                    // OTP sent — show an input field for the code
                }
                is Result.Failure -> {
                    // Handle error: result.error.message
                }
            }
            ```
          </Step>

          <Step title="Enter the OTP sent to the user's email">
            ```kotlin theme={null}
            import com.crossmint.kotlin.auth.CrossmintAuthManager
            import com.crossmint.kotlin.auth.models.OTPAuthenticationStatus
            import com.crossmint.kotlin.compose.LocalCrossmintSDK

            val authManager = LocalCrossmintSDK.current.authManager as CrossmintAuthManager
            val email = "user@example.com"
            val otpCode = "123456" // code entered by the user

            when (val result = authManager.verifyOtp(email, otpCode)) {
                is Result.Success -> {
                    when (result.value) {
                        OTPAuthenticationStatus.AUTHENTICATED -> {
                            // User is authenticated, proceed to create wallet
                        }
                        OTPAuthenticationStatus.INVALID_CODE -> {
                            // Wrong code — ask the user to try again
                        }
                        else -> { /* handle other states */ }
                    }
                }
                is Result.Failure -> {
                    // Handle error: result.error.message
                }
            }
            ```
          </Step>
        </Steps>

        <Note>
          See the <a href="https://github.com/Crossmint/android-quickstart" target="_blank">Android Demo App</a> repository for a complete UI implementation example with ViewModels and Compose screens.
        </Note>
      </Tab>

      <Tab title="Third-Party Auth (Prod)">
        If you have an existing auth provider (Auth0, Firebase, Supabase, etc.), authenticate users with your provider and pass the JWT to Crossmint.

        ```kotlin theme={null}
        import com.crossmint.kotlin.auth.CrossmintAuthManager
        import com.crossmint.kotlin.auth.models.AuthToken
        import com.crossmint.kotlin.compose.LocalCrossmintSDK

        val authManager = LocalCrossmintSDK.current.authManager as CrossmintAuthManager

        // After user authenticates with your provider, get their JWT
        val token = AuthToken(
            jwt = yourProviderJWT,
            refreshToken = yourProviderRefreshToken, // optional
            userEmail = userEmail // optional
        )

        when (val result = authManager.authenticateWithToken(token)) {
            is Result.Success -> {
                // User authenticated with Crossmint
            }
            is Result.Failure -> {
                // Handle error: result.error.message
            }
        }
        ```

        <Note>
          Configure JWT authentication in the [Crossmint Console](https://console.crossmint.com) under API Keys → JWT Authentication before using third-party auth. See the [Crossmint Demo App](https://github.com/Crossmint/crossmint-kotlin-sdk/tree/main/crossmintDemoApp) for a complete Supabase integration example.
        </Note>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Create a wallet">
    After authentication, create the user's wallet. This example uses Base Sepolia (Base testnet) but you can choose any [supported chain](/introduction/supported-chains).

    ```kotlin theme={null}
    import com.crossmint.kotlin.auth.CrossmintAuthManager
    import com.crossmint.kotlin.compose.LocalCrossmintSDK
    import com.crossmint.kotlin.signers.SignerType
    import com.crossmint.kotlin.types.EVMChain

    val sdk = LocalCrossmintSDK.current
    val authManager = sdk.authManager as CrossmintAuthManager
    val crossmintWallets = sdk.crossmintWallets

    val email = authManager.authState.value.email ?: return

    when (val result = crossmintWallets.createWallet(
        chain = EVMChain.BaseSepolia,
        recovery = SignerType.Email(email)
    )) {
        is Result.Success -> {
            val wallet = result.value // Wallet is ready to use!
        }
        is Result.Failure -> {
            // Handle error: result.error.message
        }
    }
    ```

    <Note>
      Use `getWallet(EVMChain.BaseSepolia)` for returning users to fetch their existing wallet without creating a new one.
    </Note>
  </Step>

  <Step title="Check wallet balance">
    Before sending tokens, check the wallet balance:

    ```kotlin theme={null}
    when (val result = wallet.balances()) {
        is Result.Success -> {
            val balances = result.value

            // Native token (ETH on Base Sepolia)
            println("ETH: ${balances.nativeToken.amount}")

            // USDC balance
            println("USDC: ${balances.usdc.amount}")

            // All other tokens in this wallet
            for (token in balances.tokens) {
                println("${token.name}: ${token.amount}")
            }
        }
        is Result.Failure -> {
            // Handle error: result.error.message
        }
    }
    ```
  </Step>

  <Step title="Fund the wallet">
    Before sending USDC, you need to get some into the wallet.

    **Option 1: Use the staging fund method (staging only)**

    ```kotlin theme={null}
    when (val result = wallet.fund(token = "usdxm", amount = 10, chain = "base-sepolia")) {
        is Result.Success -> { /* Funded! */ }
        is Result.Failure -> {
            // Handle error: result.error.message
        }
    }
    ```

    **Option 2: Use the USDC Faucet**

    Get the wallet address and use the <a href="https://faucet.circle.com/" target="_blank">USDC Faucet</a>:

    ```kotlin theme={null}
    val walletAddress = wallet.address
    ```

    Navigate to the faucet, select **Base Sepolia**, and enter the wallet address.

    <img src="https://mintcdn.com/crossmint/eYDj8xN_FdfDDRqk/images/wallets/circle-usdc-faucet.png?fit=max&auto=format&n=eYDj8xN_FdfDDRqk&q=85&s=5e7b583883acb530b97680bb34acf72f" alt="Circle faucet — Base Sepolia selected" width="1136" height="1088" data-path="images/wallets/circle-usdc-faucet.png" />
  </Step>

  <Step title="Send USDC">
    Send USDC to another wallet using the `wallet.send` function:

    ```kotlin theme={null}
    val recipient = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb2"
    val tokenLocator = "base-sepolia:usdc"
    val amount = 0.01

    when (val sendResult = wallet.send(recipient, tokenLocator, amount)) {
        is Result.Failure -> {
            // Handle failure
        }
        is Result.Success -> {
            when (val approveResult = wallet.approve(sendResult.value.id)) {
                is Result.Success -> {
                    // Transaction successful
                }
                is Result.Failure -> {
                    // Handle failure
                }
            }
        }
    }
    ```

    The very first time that a wallet sends a transaction on this device, an OTP will be required, sent to the user via email.

    Following transactions sent from the same device will not need OTP verification.

    To handle the OTP flow, collect the `isOTPRequired` flow from the SDK in your composable:

    ```kotlin theme={null}
    import androidx.compose.runtime.collectAsState
    import androidx.compose.runtime.rememberCoroutineScope
    import com.crossmint.kotlin.Crossmint
    import com.crossmint.kotlin.compose.CrossmintNonCustodialSignerProvider
    import com.crossmint.kotlin.compose.LocalCrossmintSDK
    import kotlinx.coroutines.launch

    @Composable
    fun QuickstartApp() {
        CrossmintNonCustodialSignerProvider(sdk = Crossmint.instance) {
            val sdk = LocalCrossmintSDK.current
            val otpRequired by sdk.isOTPRequired.collectAsState(initial = false)
            val scope = rememberCoroutineScope()

            AppContent()

            if (otpRequired) {
                OTPDialog(
                    onOTPSubmit = { otp -> scope.launch { sdk.submit(otp) } },
                    onDismiss = { scope.launch { sdk.cancelTransaction() } }
                )
            }
        }
    }
    ```

    Here is a simple OTP dialog component you can use:

    <Accordion title="OTP Sample code">
      ```kotlin theme={null}
      @Composable
      fun OTPDialog(
          onOTPSubmit: (String) -> Unit,
          onDismiss: () -> Unit,
      ) {
          var otpCode by remember { mutableStateOf("") }

          AlertDialog(
              onDismissRequest = onDismiss,
              title = { Text("Enter OTP Code") },
              text = {
                  TextField(
                      value = otpCode,
                      onValueChange = { otpCode = it },
                      label = { Text("Enter OTP") },
                      singleLine = true,
                  )
              },
              confirmButton = {
                  Button(onClick = { onOTPSubmit(otpCode) }) {
                      Text("Submit")
                  }
              },
              dismissButton = {
                  TextButton(onClick = onDismiss) {
                      Text("Cancel")
                  }
              },
          )
      }
      ```
    </Accordion>

    And that is it! You just created a non-custodial wallet for your user and made a USDC transaction on Base Sepolia.
  </Step>
</Steps>

## Launching in Production

For production, the steps are almost identical, but some changes are required:

1. Create a developer account on the [production console](https://www.crossmint.com/console)
2. Create a production client API key on the [API Keys](https://www.crossmint.com/console/projects/apiKeys) page with the API scopes `users.create`, `users.read`, `wallets.read`, `wallets.create`, `wallets:transactions.create`, `wallets:transactions.sign`, `wallets:balance.read`, `wallets.fund`
3. Update your `local.properties` with the production API key
4. **Use your own authentication provider**: For production applications, Crossmint recommends using [third-party authentication](/wallets/guides/bring-your-own-auth) (Option 2 from Step 4) with providers like Auth0, Firebase, or Supabase, rather than Crossmint Auth (OTP). Configure JWT authentication in the [Crossmint Console](https://www.crossmint.com/console/projects/apiKeys) under API Keys → JWT Authentication.
