RAIN_. iOS throws RainSDKError (a Swift enum conforming to LocalizedError); Android throws RainError (a sealed class). Every error exposes an error code string for logging and programmatic handling.
Error codes
Every code below is a specific, catchable case:| Code | iOS (RainSDKError) | Android (RainError) | Meaning |
|---|---|---|---|
| RAIN_101 | .sdkNotInitialized | SdkNotInitialized | A method was called before the SDK was built, or no wallet provider is resolved. |
| RAIN_102 | .invalidConfig / .providerNotRegistered | InvalidConfig | Invalid RPC URL, chain ID, or address, or no provider is registered for the requested id or capability. |
| RAIN_103 | .invalidRpcUrl | InvalidRpcUrl | An RPC URL could not be parsed as a valid URL. |
| RAIN_104 | .rainApiNotConfigured | ApiNotConfigured | A Rain API method was called before configureRainApi / rainApiCredentials. |
| RAIN_201 | .tokenExpired | TokenExpired | The wallet provider session token has expired or is no longer valid. |
| RAIN_202 | .unauthorized | Unauthorized | Invalid Rain API key or session, or insufficient permissions. |
| RAIN_301 | .networkError | NetworkError | Connectivity issue reaching an RPC endpoint or provider API. |
| RAIN_302 | .apiError(statusCode:message:) | ApiError | Non-auth HTTP failure from the Rain API (carries the status code). |
| RAIN_303 | .signatureNotReady(status:retryAfter:) | SignatureNotReady | Rain hasn’t produced the withdrawal signature yet (carries status and optional retryAfter). |
| RAIN_304 | .noCollateralContracts | NoCollateralContracts | The user has no collateral contracts. |
| RAIN_401 | .userRejected | UserRejected | The user cancelled the signing request in the wallet. |
| RAIN_402 | .insufficientFunds(required:available:) | InsufficientFunds | Wallet balance too low for the amount or gas fees. |
| RAIN_403 | .transactionSimulationFailed | TransactionSimulationFailed | Preflight simulation (eth_call) reverted before submission. |
| RAIN_404 | .walletUnavailable | WalletUnavailable | The provider returned no usable wallet address (user hasn’t created or connected a wallet). |
| RAIN_405 | .withdrawalRevertedByNetwork | WithdrawalRevertedByNetwork | The withdrawal reverted on-chain: often a duplicate withdrawal or an already-used signature. |
| RAIN_406 | .invalidAmount(amount:reason:) | InvalidAmount | The amount is invalid for the token: more decimals than supported, or negative. |
| RAIN_407 | .walletNotAuthorized(walletAddress:proxyAddress:) | WalletNotAuthorized | The signing wallet is not in the collateral’s admin set. |
| RAIN_501 | .providerError | ProviderError | An unhandled error from the wallet provider. |
| RAIN_502 | .internalLogicError | InternalError | EIP-712 encoding, ABI encoding, or internal state error. |
Handling errors
Switch on the specific cases you need to handle, with a default case for the rest:do {
let txHash = try await client.withdrawCollateral(/* ... */)
} catch let error as RainSDKError {
switch error {
case .sdkNotInitialized:
break // Rebuild the SDK and resolve a client
case .tokenExpired:
break // Re-authenticate the wallet provider
case .userRejected:
break // Show a retry prompt, expected user behavior
case .walletNotAuthorized(let wallet, let proxy):
print("wallet \(wallet) is not admin of \(proxy)")
case .signatureNotReady(_, let retryAfter):
scheduleRetry(after: retryAfter ?? 5)
case .insufficientFunds(let required, let available):
print("Need \(required), have \(available)")
case .withdrawalRevertedByNetwork:
break // Request a fresh withdrawal signature and retry
case .networkError(let underlying):
print("Network error: \(underlying.localizedDescription)")
default:
print("Error: \(error.errorCode)")
}
}
try {
val result = client.withdrawCollateral(/* ... */)
} catch (e: RainError) {
when (e) {
is RainError.SdkNotInitialized -> { /* Rebuild the SDK */ }
is RainError.TokenExpired -> { /* Re-authenticate the wallet provider */ }
is RainError.UserRejected -> { /* Show a retry prompt */ }
is RainError.WalletNotAuthorized -> Log.w(TAG, "wallet ${e.walletAddress} is not admin of ${e.proxyAddress}")
is RainError.SignatureNotReady -> retryAfter(e.retryAfter ?: 5)
is RainError.InsufficientFunds -> { /* Show insufficient balance UI */ }
is RainError.WithdrawalRevertedByNetwork -> { /* Request a fresh signature */ }
is RainError.NetworkError -> { /* Retry or show connectivity error */ }
else -> { println("Error: ${e.errorCode.code}") }
}
}
Accessing the error code
Every error exposes a string code (for example"RAIN_101") for logging or analytics:
catch let error as RainSDKError {
let code = error.errorCode // "RAIN_101"
let message = error.localizedDescription
}
catch (e: RainError) {
val code = e.errorCode.code // "RAIN_101"
val message = e.message
}
Troubleshooting
Common symptoms and their fixes:| Symptom | Likely cause | Fix |
|---|---|---|
| RAIN_101 on every call | SDK not built or no provider resolved | Call build() and resolve a client (provider(...)) before other methods |
| RAIN_102 at startup | Malformed RPC URL / chain ID, or unknown provider id | Verify your rpcEndpoints and that the provider is registered |
| RAIN_104 on contract/signature fetch | Rain API credentials not set | Call rainApiCredentials(...) or configureRainApi(...) (see the built-in Rain API client) |
| RAIN_201 after an idle period | Provider session expired | Re-authenticate the wallet and rebuild the SDK |
| RAIN_202 on a Rain API call | Invalid API key or session | Check the key (the SDK already retried the session mint once) |
| RAIN_303 fetching a signature | Signature still being produced | Retry after the retryAfter hint |
| RAIN_401 during withdrawal | User cancelled signing | Show a retry prompt (this is expected) |
| RAIN_403 before send | Transaction would revert | Verify parameters, balances, and that the nonce hasn’t been used |
| RAIN_404 on balance query | No wallet resolved | Resolve the client with provider(...) before querying balances |
| RAIN_405 on withdrawal | Duplicate / used signature | Request a new withdrawal signature from the Rain API |
| RAIN_407 on withdrawal | Wrong signing wallet | Withdraw with a wallet listed in the contract’s adminAddresses |
What’s next
Embedded Wallets Overview
Install the SDK and set up embedded wallets.
Balances & Transactions
Query balances and send tokens.
Withdraw Collateral
Execute collateral withdrawals from your app.