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.
Before you start
Set up your project and get an API key.
Android Demo App
See a full working example with a repository to clone.
1
Install the SDK
Add the Crossmint dependencies to your app’s build.gradle.kts file:
The Crossmint SDK requires a client API key for authentication. Get your client API key using the Crossmint Console.
3
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 but you can use any authentication provider of your choice.
Then wrap your top-level composable with CrossmintNonCustodialSignerProvider:
@Composablefun QuickstartApp() { CrossmintNonCustodialSignerProvider(sdk = Crossmint.instance) { // Your app content goes here AppContent() }}
The environment (staging vs production) is automatically determined by your API key. Staging keys start with ck_staging_, production keys with ck_production_.
Actions within the SDK are user-based, so first you need to authenticate a user.
Choose your authentication method:
Crossmint Auth (OTP)
Third-Party Auth (Prod)
Use Crossmint’s built-in OTP authentication for easy, email-based login.
1
Send OTP to user's email
import com.crossmint.kotlin.auth.CrossmintAuthManagerimport com.crossmint.kotlin.compose.LocalCrossmintSDKval authManager = LocalCrossmintSDK.current.authManager as CrossmintAuthManagerval 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 }}
2
Enter the OTP sent to the user's email
import com.crossmint.kotlin.auth.CrossmintAuthManagerimport com.crossmint.kotlin.auth.models.OTPAuthenticationStatusimport com.crossmint.kotlin.compose.LocalCrossmintSDKval authManager = LocalCrossmintSDK.current.authManager as CrossmintAuthManagerval email = "user@example.com"val otpCode = "123456" // code entered by the userwhen (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 }}
See the Android Demo App repository for a complete UI implementation example with ViewModels and Compose screens.
If you have an existing auth provider (Auth0, Firebase, Supabase, etc.), authenticate users with your provider and pass the JWT to Crossmint.
import com.crossmint.kotlin.auth.CrossmintAuthManagerimport com.crossmint.kotlin.auth.models.AuthTokenimport com.crossmint.kotlin.compose.LocalCrossmintSDKval authManager = LocalCrossmintSDK.current.authManager as CrossmintAuthManager// After user authenticates with your provider, get their JWTval 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 }}
Configure JWT authentication in the Crossmint Console under API Keys → JWT Authentication before using third-party auth. See the Crossmint Demo App for a complete Supabase integration example.
5
Create a wallet
After authentication, create the user’s wallet. This example uses Base Sepolia (Base testnet) but you can choose any supported chain.
import com.crossmint.kotlin.auth.CrossmintAuthManagerimport com.crossmint.kotlin.compose.LocalCrossmintSDKimport com.crossmint.kotlin.signers.SignerTypeimport com.crossmint.kotlin.types.EVMChainval sdk = LocalCrossmintSDK.currentval authManager = sdk.authManager as CrossmintAuthManagerval crossmintWallets = sdk.crossmintWalletsval email = authManager.authState.value.email ?: returnwhen (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 }}
Use getWallet(EVMChain.BaseSepolia) for returning users to fetch their existing wallet without creating a new one.
6
Check wallet balance
Before sending tokens, check the wallet balance:
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 }}
7
Fund the wallet
Before sending USDC, you need to get some into the wallet.Option 1: Use the staging fund method (staging only)
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 FaucetGet the wallet address and use the USDC Faucet:
val walletAddress = wallet.address
Navigate to the faucet, select Base Sepolia, and enter the wallet address.
8
Send USDC
Send USDC to another wallet using the wallet.send function:
val recipient = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb2"val tokenLocator = "base-sepolia:usdc"val amount = 0.01when (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:
Create a production client API key on the API Keys page with the API scopes users.create, users.read, wallets.read, wallets.create, wallets:transactions.create, wallets:transactions.sign, wallets:balance.read, wallets.fund
Update your local.properties with the production API key
Use your own authentication provider: For production applications, Crossmint recommends using third-party authentication (Option 2 from Step 4) with providers like Auth0, Firebase, or Supabase, rather than Crossmint Auth (OTP). Configure JWT authentication in the Crossmint Console under API Keys → JWT Authentication.