This document describes the intended public API for external consumers of the OMS Client Kotlin SDK.
implementation("io.github.0xsequence:oms-client-kotlin-sdk:0.1.0-alpha.2")Consumer apps need Android minSdk 26, Android compileSdk 34 or newer, and
Java 17 Android compile options. Updating compileSdk is separate from
targetSdk; consumers do not need to opt into a newer Android runtime behavior
just to consume the SDK.
The published SDK is a single Maven artifact. Generated WaaS WebRPC classes are
embedded in the AAR under com.omsclient.kotlin_sdk.internal.generated.waas and
are not part of the documented public API. Consumers should not add or depend on
an oms-client-kotlin-sdk-waas-generated artifact.
OMSClient(
context: Context,
publishableKey: String,
projectId: String,
environment: OMSClientEnvironment = OMSClientEnvironment(),
okHttpClient: OkHttpClient = OkHttpClient(),
)val client.wallet: WalletClient
val client.indexer: IndexerClientval client.session: OMSClientSessionStatedata class OMSClientSessionState(
val walletAddress: String?,
val expiresAt: Instant?,
val loginType: OMSClientSessionLoginType?,
val sessionEmail: String?,
)data class OMSClientSessionExpiredEvent(
val session: OMSClientSessionState,
val expiredAt: Instant,
)enum class OMSClientSessionLoginType {
Email,
GoogleAuth,
Oidc,
}client.session only reports completed wallet-session state. Pending auth
state, OIDC redirect verifier/state, and signer details are SDK internals. Apps
should show OTP or redirect waiting UI from the method result that started the
flow, not from session state. Always pass incoming app-link URLs to
handleOidcRedirectCallback; stale callbacks return NoPendingAuth, and the
app can show sign-in UI and let the user start again. Persisted session restore
revives completed wallet sessions, including the session expiry, login type, and
email returned by the wallet API, but not pending email OTP state. Completed auth
requests use a one-week wallet API session lifetime by default
(WalletClient.DEFAULT_SESSION_LIFETIME_SECONDS, 604_800 seconds); pass
sessionLifetimeSeconds to auth completion methods to request a different
positive whole-number lifetime in seconds. Invalid values return
OmsSdkErrorCode.ValidationError before an auth completion request is sent. Auth
completion loads all wallet pages before selecting or creating a wallet. If auth
completes but wallet selection or session persistence fails, the SDK clears the
in-memory auth session instead of leaving transient state active.
Starting a new email, OIDC ID-token, or OIDC redirect auth flow replaces any
existing wallet session so expired or stale sessions do not block
re-authentication.
Expired sessions are made inactive before protected wallet operations and throw
OmsSessionException with code = OmsSdkErrorCode.SessionExpired. The SDK
clears active signer/session state, but keeps expired completed-session metadata
in storage until the app starts a new auth flow or calls signOut(). Use
onSessionExpired to route users back to sign-in; the event includes the
expired session snapshot so apps can reuse sessionEmail for email OTP reauth or
as a Google loginHint, including after process recreation.
The Android OMSClient(context, ...) constructor wires two separate
Android-backed pieces:
- an Android Keystore P-256 credential signer authorizes wallet API requests
with
ecdsa-p256-sha256; the private key is non-extractable and is not session metadata - a session metadata store persists completed-session metadata in an app-private no-backup file; temporary OIDC redirect state uses a separate no-backup file
Completed-session metadata is limited to restorable wallet state: wallet
id/address, signer address/algorithm, expiry, login type, and optional email. It
is not wallet authorization material: by itself it cannot sign requests or
access a wallet. Restore succeeds only while the matching Keystore credential
still exists, and wallet operations must sign fresh requests with that
credential. publishableKey is sent as X-Access-Key; projectId is used as the
wallet request signing scope and OIDC redirect state scope.
fun client.wallet.signOut()fun client.wallet.onSessionExpired(
listener: (OMSClientSessionExpiredEvent) -> Unit,
): () -> UnitRegisters a listener for expired wallet sessions and returns an unsubscribe
function. The wallet client stores the latest expired-session event and replays
it to each new listener until a new auth flow, new wallet session, or
signOut() clears it. Listeners are delivered on the Android main thread.
suspend fun client.wallet.startEmailAuth(
email: String,
)suspend fun client.wallet.signInWithOidcIdToken(
idToken: String,
issuer: String,
audience: String,
walletSelection: WalletSelectionBehavior = WalletSelectionBehavior.Automatic,
walletType: WalletType = WalletType.Ethereum,
sessionLifetimeSeconds: Long = WalletClient.DEFAULT_SESSION_LIFETIME_SECONDS,
): CompleteAuthResultPass walletSelection = WalletSelectionBehavior.Manual for OIDC ID-token auth
when the app needs to show its own wallet-selection UI before selecting or
creating a wallet.
data class OidcProviderConfig(
val issuer: String,
val clientId: String,
val authorizationUrl: String,
val scopes: List<String> = listOf("openid", "email", "profile"),
val relayRedirectUri: String? = null,
val authorizeParams: Map<String, String> = emptyMap(),
)object OidcProviders {
fun google(
clientId: String = OidcProviders.defaultGoogleClientId,
relayRedirectUri: String = OidcProviders.defaultRelayRedirectUri,
scopes: List<String> = listOf("openid", "email", "profile"),
authorizeParams: Map<String, String> = emptyMap(),
): OidcProviderConfig
}data class StartOidcRedirectAuthResult(
val authorizationUrl: String,
val state: String,
val challenge: String,
)suspend fun client.wallet.startOidcRedirectAuth(
provider: OidcProviderConfig,
redirectUri: String,
walletType: WalletType = WalletType.Ethereum,
relayRedirectUri: String? = provider.relayRedirectUri,
authorizeParams: Map<String, String> = emptyMap(),
loginHint: String? = null,
): StartOidcRedirectAuthResultsealed interface OidcRedirectAuthResult {
data class Completed(val wallet: Wallet) : OidcRedirectAuthResult
data class WalletSelection(
val pendingSelection: PendingWalletSelection,
) : OidcRedirectAuthResult
data object NotOidcRedirectCallback : OidcRedirectAuthResult
data object NoPendingAuth : OidcRedirectAuthResult
data class Failed(val error: Throwable) : OidcRedirectAuthResult
}suspend fun client.wallet.handleOidcRedirectCallback(
callbackUrl: String?,
walletSelection: WalletSelectionBehavior = WalletSelectionBehavior.Automatic,
sessionLifetimeSeconds: Long = WalletClient.DEFAULT_SESSION_LIFETIME_SECONDS,
): OidcRedirectAuthResultOIDC redirect auth stores transient verifier/state data separately from the
completed wallet session so Android can resume after the browser redirect. Open
StartOidcRedirectAuthResult.authorizationUrl with app-owned UI such as Custom
Tabs, then pass incoming app-link URLs to handleOidcRedirectCallback. The
handler is idempotent and safe to call from onCreate / onNewIntent: unrelated
links return NotOidcRedirectCallback, stale links return NoPendingAuth, and
provider or completion failures return Failed with an OmsSdkException when
the SDK can classify the failure. With
WalletSelectionBehavior.Automatic, successful callbacks return Completed.
With WalletSelectionBehavior.Manual, successful callbacks return
WalletSelection. Starting a new auth flow clears or replaces stale redirect
state, and signOut() clears it.
Pass loginHint to startOidcRedirectAuth only when you want to prefill or
select a specific Google account, such as during session-expiry reauth. The SDK
only sends login_hint for providers whose issuer is
https://accounts.google.com. If omitted, the SDK falls back to the previous
active session email when one exists before the redirect auth attempt starts.
Pass an empty string to force no login_hint for a call. After signOut(), the
previous session email is cleared.
enum class WalletSelectionBehavior {
Automatic,
Manual,
}class PendingWalletSelection {
val walletType: WalletType
val wallets: List<Wallet>
val credential: CredentialInfo
suspend fun selectWallet(walletId: String): WalletSelectionResult
suspend fun createAndSelectWallet(reference: String? = null): WalletSelectionResult
}sealed interface CompleteAuthResult {
data class WalletSelected(
val walletAddress: String,
val wallet: Wallet,
val wallets: List<Wallet>,
val credential: CredentialInfo,
) : CompleteAuthResult
data class WalletSelection(
val pendingSelection: PendingWalletSelection,
) : CompleteAuthResult
}suspend fun client.wallet.completeEmailAuth(
code: String,
walletSelection: WalletSelectionBehavior = WalletSelectionBehavior.Automatic,
walletType: WalletType = WalletType.Ethereum,
sessionLifetimeSeconds: Long = WalletClient.DEFAULT_SESSION_LIFETIME_SECONDS,
): CompleteAuthResultAuth completion loads all wallet pages before selecting or creating a wallet.
walletType defines which wallet type is eligible.
In WalletSelectionBehavior.Automatic, auth completion:
- creates and selects a wallet when no wallet matches
walletType - selects the first matching wallet returned by WaaS when one or more wallets
match
walletType
Automatic email and OIDC ID-token auth return
CompleteAuthResult.WalletSelected on success. Automatic OIDC redirect auth
returns OidcRedirectAuthResult.Completed on success. Automatic mode does not
fall back to WalletSelection; use manual mode when the user should choose
between wallets.
In WalletSelectionBehavior.Manual, auth completion returns
CompleteAuthResult.WalletSelection or OidcRedirectAuthResult.WalletSelection
with a PendingWalletSelection. No wallet is selected or created until the app
calls pendingSelection.selectWallet(...) or
pendingSelection.createAndSelectWallet(...). pendingSelection.wallets
contains existing wallets filtered to walletType.
Use manual mode up front for apps that need to support choosing between multiple wallets for the same wallet type.
val client.wallet.walletAddress: String?data class WalletSelectionResult(
val walletAddress: String,
val wallet: Wallet,
)suspend fun client.wallet.listWallets(): List<Wallet>suspend fun client.wallet.useWallet(
walletId: String,
): WalletSelectionResultsuspend fun client.wallet.createWallet(
walletType: WalletType = WalletType.Ethereum,
reference: String? = null,
): WalletSelectionResultsuspend fun client.wallet.signMessage(
network: Network,
message: String,
): Stringsuspend fun client.wallet.signTypedData(
network: Network,
typedData: JsonElement,
): Stringsuspend fun client.wallet.isValidMessageSignature(
network: Network,
message: String,
signature: String,
): Booleansuspend fun client.wallet.isValidTypedDataSignature(
network: Network,
typedData: JsonElement,
signature: String,
): Booleansuspend fun client.wallet.sendTransaction(
network: Network,
to: String,
value: BigInteger,
waitForStatus: Boolean = true,
statusPolling: TransactionStatusPollingOptions? = null,
selectFeeOption: FeeOptionSelector? = null,
): SendTransactionResponsesuspend fun client.wallet.sendTransaction(
network: Network,
request: SendTransactionRequest,
waitForStatus: Boolean = true,
statusPolling: TransactionStatusPollingOptions? = null,
selectFeeOption: FeeOptionSelector? = null,
): SendTransactionResponsesuspend fun client.wallet.callContract(
network: Network,
contract: String,
method: String,
args: List<AbiArg>? = null,
mode: TransactionMode = TransactionMode.Relayer,
waitForStatus: Boolean = true,
statusPolling: TransactionStatusPollingOptions? = null,
selectFeeOption: FeeOptionSelector? = null,
): SendTransactionResponsesuspend fun client.wallet.getTransactionStatus(
txnId: String,
): TransactionStatusResponsesuspend fun client.wallet.listAccess(
pageSize: UInt? = null,
): List<CredentialInfo>fun client.wallet.listAccessPages(
pageSize: UInt? = null,
): Flow<ListAccessResponse>suspend fun client.wallet.listAccessPage(
pageSize: UInt? = null,
cursor: String? = null,
): ListAccessResponsesuspend fun client.wallet.getIdToken(
ttlSeconds: UInt? = null,
customClaims: Map<String, JsonElement>? = null,
): Stringsuspend fun client.wallet.revokeAccess(
targetCredentialId: String,
)When a prepared transaction includes fee options, selectFeeOption receives the
available options enriched with the selected wallet's matching token balance
when available. When no selector is provided, sendTransaction uses the first
required fee option, or no fee option when the transaction is sponsored.
Use FeeOptionSelector.firstAvailable to select the first option whose enriched
raw balance covers the quoted fee. Sponsored transactions skip fee selection;
unsponsored transactions fail before execute when no fee option exists or the
selector returns null.
value is a raw base-unit integer; use parseUnits to convert human-entered
decimal values before sending.
After execution, sendTransaction and callContract poll the WaaS status
endpoint briefly for an executed status or transaction hash. Pass
waitForStatus = false to return immediately after execute, or pass
statusPolling to tune the fast poll count, intervals, and timeout. If the
transaction remains pending when polling times out, the response contains the
txnId, status = TransactionStatus.Pending, and txnHash = null.
Use getTransactionStatus to refresh a transaction later. listAccess follows
WaaS cursors and returns all credentials, listAccessPages emits each page as a
Flow, and listAccessPage exposes one page at a time for manual cursor
pagination. Pass pageSize when fetching credentials that may span multiple
pages so each request uses an explicit limit.
val client.supportedNetworks: List<Network>
val supportedNetworks: List<Network>
fun findNetworkById(id: Int): Network?
fun findNetworkByName(name: String): Network?data class Network(
val id: Int,
val name: String,
val nativeTokenSymbol: String,
val explorerUrl: String,
val displayName: String = name,
)
Network.MAINNET
Network.SEPOLIA
Network.POLYGON
Network.AMOY
Network.ARBITRUM
Network.ARBITRUM_SEPOLIA
Network.OPTIMISM
Network.OPTIMISM_SEPOLIA
Network.BASE
Network.BASE_SEPOLIA
Network.BSC
Network.BSC_TESTNET
Network.ARBITRUM_NOVA
Network.AVALANCHE
Network.AVALANCHE_TESTNET
Network.KATANAEach entry exposes id, name, nativeTokenSymbol, explorerUrl, and
displayName. name is also the registry/routing slug for indexer and node
URLs, while displayName is the user-facing label. Ethereum mainnet uses
name = "mainnet" and displayName = "Ethereum".
Top-level unit helpers live in com.omsclient.kotlin_sdk.utils.
fun formatUnits(
value: BigInteger,
decimals: Int,
): Stringfun parseUnits(
value: String,
decimals: Int,
): BigIntegersuspend fun indexer.getTokenBalances(
network: Network,
contractAddress: String? = null,
walletAddress: String,
includeMetadata: Boolean,
page: TokenBalancesPageRequest = TokenBalancesPageRequest(),
): TokenBalancesResultsuspend fun indexer.getNativeTokenBalance(
network: Network,
walletAddress: String,
): TokenBalance?contractAddress can be omitted to query balances across token contracts.
page defaults to page 0 with page size 40. Pass
includeMetadata = true when callers need TokenContractInfo or
TokenMetadata fields on returned balances. getNativeTokenBalance returns
null when the indexer response has no native balance object. The wallet client
also uses it internally to enrich fee option balances.
class OMSClientEnvironment(
val walletApiUrl: String = OMSClientEnvironment.walletApiUrlDefault,
val apiRpcUrl: String = OMSClientEnvironment.apiRpcUrlDefault,
val indexerUrlTemplate: String = OMSClientEnvironment.indexerUrlTemplateDefault,
)walletApiUrl should be treated as the Wallet API base URL/origin. Wallet RPC method paths come from the generated waas schema.
fun OMSClientEnvironment.Companion.demoDefaults(): OMSClientEnvironmentPublic SDK APIs throw OmsSdkException when the SDK can classify a failure
without exposing generated WebRPC internals.
enum class OmsSdkErrorCode {
HttpError,
InvalidResponse,
RequestFailed,
AuthCommitmentConsumed,
SessionMissing,
SessionExpired,
WalletSelectionStale,
WalletSelectionUnavailable,
WalletSelectionInFlight,
TransactionExecutionUnconfirmed,
TransactionStatusLookupFailed,
ValidationError,
}open class OmsSdkException(
val code: OmsSdkErrorCode,
val operation: OmsSdkOperation?,
val status: Int?,
val txnId: String?,
val retryable: Boolean?,
val upstreamError: OmsUpstreamError?,
) : RuntimeExceptionenum class OmsUpstreamService {
Waas,
Indexer,
}
data class OmsUpstreamError(
val service: OmsUpstreamService,
val name: String?,
val code: String?,
val message: String?,
val status: Int?,
)enum class OmsSdkOperation(
val id: String,
) {
PendingWalletSelection("wallet.pendingWalletSelection"),
PendingWalletSelectionCreateAndSelectWallet("wallet.pendingWalletSelection.createAndSelectWallet"),
PendingWalletSelectionSelectWallet("wallet.pendingWalletSelection.selectWallet"),
IndexerGetNativeTokenBalance("indexer.getNativeTokenBalance"),
IndexerGetTokenBalances("indexer.getTokenBalances"),
WalletCallContract("wallet.callContract"),
WalletCompleteEmailAuth("wallet.completeEmailAuth"),
WalletCreateWallet("wallet.createWallet"),
WalletExecute("wallet.execute"),
WalletGetIdToken("wallet.getIdToken"),
WalletHandleOidcRedirectCallback("wallet.handleOidcRedirectCallback"),
WalletGetTransactionStatus("wallet.getTransactionStatus"),
WalletIsValidMessageSignature("wallet.isValidMessageSignature"),
WalletIsValidTypedDataSignature("wallet.isValidTypedDataSignature"),
WalletListAccess("wallet.listAccess"),
WalletListAccessPage("wallet.listAccessPage"),
WalletListAccessPages("wallet.listAccessPages"),
WalletListWallets("wallet.listWallets"),
WalletRevokeAccess("wallet.revokeAccess"),
WalletSendTransaction("wallet.sendTransaction"),
WalletSignInWithOidcIdToken("wallet.signInWithOidcIdToken"),
WalletSignMessage("wallet.signMessage"),
WalletSignTypedData("wallet.signTypedData"),
WalletStartEmailAuth("wallet.startEmailAuth"),
WalletStartOidcRedirectAuth("wallet.startOidcRedirectAuth"),
WalletTransactionStatus("wallet.transactionStatus"),
WalletUseWallet("wallet.useWallet"),
}RequestFailed covers classified WebRPC/backend failures, including backend
error codes newer than the generated WaaS client. InvalidResponse is reserved
for malformed or unparseable responses.
upstreamError is normalized diagnostic detail from a remote OMS service response
or transport failure. Use SDK-level code for app branching; use
upstreamError for logging and service-specific troubleshooting. SDK-local
validation, session, and wallet-selection failures do not include upstream
details.
TransactionExecutionUnconfirmed means transaction preparation succeeded and
the SDK has a txnId, but the execute request failed before the SDK could
confirm whether the transaction was submitted. Do not blindly resend the same
write solely because the upstream failure looked temporary.
TransactionStatusLookupFailed means the transaction was submitted, but
post-submit status polling failed. Retry by calling getTransactionStatus with
the returned txnId; retryable describes that status lookup operation, not
the original write.
enum class WalletType {
Ethereum,
UNKNOWN_DEFAULT,
}
enum class TransactionMode {
Native,
Relayer,
UNKNOWN_DEFAULT,
}
enum class TransactionStatus {
Quoted,
Pending,
Executed,
UNKNOWN_DEFAULT,
}data class Wallet(
val id: String,
val type: WalletType,
val address: String,
val reference: String? = null,
)data class FeeToken(
val network: String,
val name: String,
val symbol: String,
val type: String,
val decimals: UInt? = null,
val logoUrl: String,
val contractAddress: String? = null,
val tokenId: String? = null,
)data class FeeOption(
val token: FeeToken,
val value: String,
val displayValue: String,
)
data class FeeOptionSelection(
val token: String,
) {
constructor(feeOption: FeeOption)
}
fun interface FeeOptionSelector {
suspend fun select(feeOptions: List<FeeOptionWithBalance>): FeeOptionSelection?
companion object {
val firstAvailable: FeeOptionSelector
}
}data class FeeOptionWithBalance(
val feeOption: FeeOption,
val balance: TokenBalance?,
val available: String?,
val availableRaw: String?,
val decimals: Int?,
) {
val selection: FeeOptionSelection
}data class SendTransactionRequest(
val to: String,
val value: BigInteger,
val data: String? = null,
val mode: TransactionMode = TransactionMode.Relayer,
)data class SendTransactionResponse(
val txnId: String,
val status: TransactionStatus,
val txnHash: String?,
)data class TransactionStatusPollingOptions(
val fastPollIntervalMillis: Long = 400L,
val fastPollCount: Int = 5,
val pollIntervalMillis: Long = 2_000L,
val timeoutMillis: Long = 60_000L,
)sendTransaction and callContract use the fast poll interval for the first
fastPollCount status attempts, then use pollIntervalMillis until
timeoutMillis. Set pollIntervalMillis <= 0 to disable slow polling after the
fast polling phase.
data class TransactionStatusResponse(
val status: TransactionStatus,
val txnHash: String? = null,
)data class AbiArg(
val type: String,
val value: JsonElement,
)data class CredentialInfo(
val credentialId: String,
val expiresAt: String,
val isCaller: Boolean,
)data class ListAccessResponse(
val credentials: List<CredentialInfo>,
val page: Page? = null,
)
data class Page(
val limit: UInt? = null,
val cursor: String? = null,
)data class TokenBalancesPageRequest(
val page: Int = 0,
val pageSize: Int = 40,
)data class TokenBalancesPage(
val page: Int,
val pageSize: Int,
val more: Boolean,
)data class TokenBalance(
val contractType: String?,
val contractAddress: String?,
val accountAddress: String?,
val tokenId: String?,
val balance: String?,
val blockHash: String?,
val blockNumber: Long?,
val chainId: Long?,
val balanceUSD: String? = null,
val priceUSD: String? = null,
val priceUpdatedAt: String? = null,
val uniqueCollectibles: String? = null,
val isSummary: Boolean? = null,
val contractInfo: TokenContractInfo? = null,
val tokenMetadata: TokenMetadata? = null,
)data class TokenContractInfo(
val chainId: Long? = null,
val address: String? = null,
val source: String? = null,
val name: String? = null,
val type: String? = null,
val symbol: String? = null,
val decimals: Int? = null,
val logoURI: String? = null,
val deployed: Boolean? = null,
val bytecodeHash: String? = null,
val extensions: Map<String, JsonElement>? = null,
val updatedAt: String? = null,
val queuedAt: String? = null,
val status: String? = null,
)data class TokenMetadata(
val chainId: Long? = null,
val contractAddress: String? = null,
val tokenId: String? = null,
val source: String? = null,
val name: String? = null,
val description: String? = null,
val image: String? = null,
val video: String? = null,
val audio: String? = null,
val properties: Map<String, JsonElement>? = null,
val attributes: List<Map<String, JsonElement>>? = null,
val imageData: String? = null,
val externalUrl: String? = null,
val backgroundColor: String? = null,
val animationUrl: String? = null,
val decimals: Int? = null,
val updatedAt: String? = null,
val assets: List<TokenMetadataAsset>? = null,
val status: String? = null,
val queuedAt: String? = null,
val lastFetched: String? = null,
)data class TokenMetadataAsset(
val id: Long? = null,
val collectionId: Long? = null,
val tokenId: String? = null,
val url: String? = null,
val metadataField: String? = null,
val name: String? = null,
val filesize: Long? = null,
val mimeType: String? = null,
val width: Int? = null,
val height: Int? = null,
val updatedAt: String? = null,
)data class TokenBalancesResult(
val status: Int,
val page: TokenBalancesPage?,
val balances: List<TokenBalance>,
)if (client.wallet.walletAddress == null) {
client.wallet.startEmailAuth("user@example.com")
// A one-time code is sent to the user's email inbox.
val result = client.wallet.completeEmailAuth("123456")
check(result is CompleteAuthResult.WalletSelected)
showWallet(result.wallet)
}For OIDC ID-token flows:
val result =
client.wallet.signInWithOidcIdToken(
idToken = googleIdToken,
issuer = "https://accounts.google.com",
audience = "YOUR_WEB_CLIENT_ID",
)
check(result is CompleteAuthResult.WalletSelected)
showWallet(result.wallet)For OIDC redirect flows, start with the default Google provider unless the app has its own web client ID or provider configuration:
val started = client.wallet.startOidcRedirectAuth(
provider = OidcProviders.google(),
redirectUri = "yourapp://auth/callback",
)
// Open started.authorizationUrl.
when (val result = client.wallet.handleOidcRedirectCallback(intent.data?.toString())) {
is OidcRedirectAuthResult.Completed -> showWallet(result.wallet)
OidcRedirectAuthResult.NotOidcRedirectCallback -> Unit
OidcRedirectAuthResult.NoPendingAuth -> Unit
is OidcRedirectAuthResult.Failed -> showRestartSignIn(result.error)
}Use a redirect URI that matches a deep link registered by your app, such as
yourapp://auth/callback. For a custom Google web client ID, call
OidcProviders.google(clientId = "YOUR_WEB_CLIENT_ID").
Use manual mode when the app needs to present wallet choices:
val result =
client.wallet.completeEmailAuth(
code = "123456",
walletSelection = WalletSelectionBehavior.Manual,
)
check(result is CompleteAuthResult.WalletSelection)
val selected = selectOrCreateWallet(result.pendingSelection)
showWallet(selected.wallet)OIDC ID-token auth uses the same walletSelection argument on
signInWithOidcIdToken(...).
The picker can always offer "Create New Wallet" as a separate option from the existing wallet list:
private suspend fun selectOrCreateWallet(
pendingSelection: PendingWalletSelection,
): WalletSelectionResult {
val choice =
showWalletPickerAndWaitForChoice(
wallets = pendingSelection.wallets,
includeCreateNewWallet = true,
)
return when (choice) {
WalletPickerChoice.CreateNew ->
pendingSelection.createAndSelectWallet()
is WalletPickerChoice.Existing ->
pendingSelection.selectWallet(choice.wallet.id)
}
}WalletPickerChoice is app UI state in this example. Both SDK calls return the
selected wallet and persist it as the active wallet session.
For OIDC redirect flows, pass the same behavior to the callback handler:
when (
val result =
client.wallet.handleOidcRedirectCallback(
callbackUrl = intent.data?.toString(),
walletSelection = WalletSelectionBehavior.Manual,
)
) {
is OidcRedirectAuthResult.WalletSelection -> {
val selected = selectOrCreateWallet(result.pendingSelection)
showWallet(selected.wallet)
}
is OidcRedirectAuthResult.Completed -> error("Expected manual wallet selection")
OidcRedirectAuthResult.NotOidcRedirectCallback -> Unit
OidcRedirectAuthResult.NoPendingAuth -> Unit
is OidcRedirectAuthResult.Failed -> showRestartSignIn(result.error)
}For raw calldata or transaction parameters beyond to and value:
val network = Network.AMOY
val txResult = client.wallet.sendTransaction(
network = network,
request = SendTransactionRequest(
to = "0xContractAddress",
value = parseUnits("0", 18),
data = "0x1234",
mode = TransactionMode.Native,
),
)For WaaS ABI-style contract calls:
val txResult = client.wallet.callContract(
network = network,
contract = "0xContractAddress",
method = "transfer(address,uint256)",
args =
listOf(
AbiArg(type = "address", value = JsonPrimitive("0xRecipient")),
AbiArg(type = "uint256", value = JsonPrimitive("1000000000000000000")),
),
)To choose a fee option before execution:
val txResult = client.wallet.sendTransaction(
network = network,
request = SendTransactionRequest(
to = "0xContractAddress",
value = parseUnits("0", 18),
data = "0x1234",
mode = TransactionMode.Native,
),
selectFeeOption = FeeOptionSelector.firstAvailable,
)Or use a custom picker:
val txResult = client.wallet.sendTransaction(
network = network,
request = SendTransactionRequest(
to = "0xContractAddress",
value = parseUnits("0", 18),
data = "0x1234",
mode = TransactionMode.Native,
),
) { feeOptions ->
val selected = showFeePickerAndWaitForChoice(feeOptions)
selected.selection
}