Before you begin
Install the Rain SDK and configure a Base Sepolia RPC endpoint. All wallet operations in this quickstart run directly in your Android application. You do not need a Rain API key. Note: Network operations use Kotlin suspend functions and must be called from a coroutine.1
Create the provider
RainProvider is the Rain wallet: it owns login, wallet creation, and sessions. The wallet backend’s identity is embedded in the SDK, so the default configuration is the product. It needs your Application for secure storage. Create one provider per app and keep it for the app’s lifetime.RainWalletConfig. The hook is held for the provider’s whole life, so don’t capture an Activity or ViewModel in it.2
Log in: restore a session or start a login
On launch, wait for the SDK to restore a persisted session. A returning user skips the code entirely. The first authentication call of a launch also runs the wallet backend’s one-time initialization.
wallet.authState is a Flow<RainWalletAuthState> (Loading, Authenticated, Unauthenticated) and wallet.currentAuthState() is its snapshot, so a Compose login screen can collect it instead of polling.3
Send a one-time code
Send a login code to the user’s email address or phone number. The contact is the account’s identity: a first login with it signs the user up, and later logins with the same contact find the same account.Calling
sendLoginCode again for the same contact issues a new code and replaces the pending one, which is how you implement Resend code. A blank email or a phone number outside E.164 throws RainError.InvalidConfig before anything leaves the device.4
Confirm the code and create the wallet
Pass the code the user typed. On a first login the SDK signs the user up and creates one wallet holding an Ethereum account and a Solana account, from a single seed, in the same request. On a returning login it finds the existing account.A wrong code keeps the pending challenge, so the user can retype it without requesting a new one. Codes expire after 5 minutes and lock after 3 wrong attempts.
The user is logged in and their wallet exists. A successful login also ends the user’s sessions on other devices; the signed-out device’s
onSessionExpired fires on its next call.5
Build the SDK and resolve the wallet
Register the provider with the chains you want to use and build the SDK. Then resolve the wallet-bound Resolving before a session is live throws
RainClient. Resolution suspends once, while the SDK materializes the wallet, and is cached afterwards.RainError.TokenExpired (RAIN_201). Send the EVM address (and the Solana address, if you use Solana) to your backend: creating the user’s Rain application requires it. See Wallet creation.6
Send a transaction
Fund the wallet with testnet USDC first (the Circle faucet supports Base Sepolia), then send some of it to another address you control. Amounts are Gas is sponsored by default, so the wallet doesn’t need ETH to send. Native sends use
BigDecimal in human units; the SDK resolves the token’s decimals and converts exactly.client.sendNative(chainId, to, amount).You’ve logged in, created a wallet, and sent a token. Everything else on
RainClient follows the same shape: balances and history, receiving funds, and card funding.Log in with a passkey instead
Users can log in with an existing passkey or add a passkey to their account for future authentication. ConfigurepasskeyDomain during SDK setup and host https://<domain>/.well-known/assetlinks.json for your app. Each passkey call takes the foreground Activity the system passkey sheet is presented from and suspends until the sheet closes, so run it in a scope that survives configuration changes, such as a ViewModel scope.
signUpWithPasskey(activity) only when creating a new account. Returning users should use loginWithPasskey(activity) to access their existing wallet.
See Authentication options for the association file shape and recovery guidance.
Log out and clean up
Uselogout() to end the user’s session and close() to release SDK resources.
rain.close() permanently closes the SDK instance. Create a new RainSdk instance before the next login. Use rain.reset() instead if you only need to clear resolved clients while keeping the provider active.
Using a third-party wallet provider
If your application already uses Portal, Privy, or your own Turnkey organization, register the corresponding provider adapter instead ofRainProvider. Authentication and session management remain with your existing provider, while subsequent wallet operations use the same RainClient interface.
What’s next
Features
What each platform and chain supports.
Testing
Testnets, test tokens, and the pre-go-live checklist.
Error reference
Every
RAIN_* code and how to handle it.