API Reference

iOS SDK API Reference

Public CROSSxSDK API reference.


Configuration

SDKConfig

SDK initialization configuration object.

public struct SDKConfig: Sendable {
    public init(
        bundle: Bundle = .main,
        projectId: String,
        appName: String,
        callbackScheme: String? = nil,
        httpNetwork: HTTPNetworkConfig? = nil,
        theme: SDKThemeMode = .system,
        themeTokens: SDKThemeTokens? = nil,
        debug: Bool = true,
        locale: SDKLocale? = nil
    )

    public static func fromInfoPlist(
        projectId: String,
        appName: String,
        bundle: Bundle = .main,
        httpNetwork: HTTPNetworkConfig? = nil
    ) throws -> SDKConfig

    public static func defaultScheme(for projectId: String) -> String
}
ParameterTypeDefaultDescription
bundleBundle.mainApp bundle; used to read Info.plist values
projectIdStringCROSSx project identifier
appNameStringHost app display name shown in SDK UI
callbackSchemeString?nilOAuth deep-link scheme. Use SDKConfig.defaultScheme(for:)
httpNetworkHTTPNetworkConfig?nilNetwork timeout and retry settings
themeSDKThemeMode.systemInitial theme mode
themeTokensSDKThemeTokens?nilColor token overrides per theme
debugBooltrueEnables SDK debug logging
localeSDKLocale?nilUI language override; nil follows device system locale

Example

let sdk = try CROSSxSDK(config: SDKConfig(
    projectId: "your-project-id",
    appName: "Your App Name",
    locale: .ko  // or .en
))

SDKLocale

Controls the language used for all SDK-provided UI strings and error messages.

public enum SDKLocale: Sendable {
    case en
    case ko
}
ValueBehavior
nil (default)Follows the device system locale
.enForces English regardless of system locale
.koForces Korean regardless of system locale

Set at initialization via SDKConfig.locale, or change at runtime with updateLocale(_:).


SDKSignInProvider

Selects the OAuth provider shown during signIn(provider:) / signInWithCreate(provider:).

public enum SDKSignInProvider: Equatable, Sendable {
    case all    // shows provider selection sheet
    case google
    case apple
}

.all shows the provider selection sheet; .google / .apple skip directly to that provider.


Theme

SDKThemeMode / SDKThemeTokens

public enum SDKThemeMode: String, Sendable {
    case light
    case dark
    case system
}

public struct SDKThemeTokens: Sendable {
    public var light: SDKColorOverrides?
    public var dark: SDKColorOverrides?
}

Pass to SDKConfig or applyTheme(themeMode:themeTokens:) to control the visual appearance of SDK modals.

SDKColorOverrides

Per-mode color overrides. All fields are optional; unset fields fall back to SDK defaults.

public struct SDKColorOverrides: Sendable {
    public var primary: String?
    public var secondary: String?
    public var onPrimary: String?
    public var borderDefault: String?
    public var borderSubtle: String?
    public var textPrimary: String?
    public var textSecondary: String?
    public var textTertiary: String?
    public var surfaceDefault: String?
    public var surfaceSubtle: String?
    public var background: String?
}

Color format: hex string (e.g. "#FF5733", "#12121280" for alpha).

HTTPNetworkConfig

public struct HTTPNetworkConfig: Sendable {
    public let timeout: TimeInterval   // default 30
    public let maxRetries: Int         // default 3
}

Core

version

static let version: String

Current SDK version string.


handleURL(_:)

@discardableResult
func handleURL(_ url: URL) -> Bool

Routes an incoming deep-link URL to the SDK's OAuth callback handler. Call this from your AppDelegate or SceneDelegate when the URL matches your callbackScheme.

Returns true if the URL was handled by the SDK, false otherwise.


initialize()

func initialize() async throws -> AuthResult?

Attempts to restore a previously stored session. If a valid session is found, also checks cloud backup status and includes it in AuthResult.cloudBackupStatus.

  • Returns AuthResult if the session was successfully restored.
  • Returns nil if no restorable session exists.
  • Call this on app start before checking isLoggedIn().

Returns AuthResult? — restored session info including cloudBackupStatus, or nil.


applyTheme(themeMode:themeTokens:)

func applyTheme(themeMode: SDKThemeMode? = nil, themeTokens: SDKThemeTokens? = nil)

Applies theme configuration to SDK modals. Takes effect from the next modal that is opened. Omitting a parameter keeps the current value.

ParameterDefaultDescription
themeModecurrent value.light / .dark / .system
themeTokenscurrent valueColor token overrides

updateLocale(_:)

func updateLocale(_ locale: SDKLocale?)

Overrides the SDK UI language at runtime. Pass nil to revert to the system locale. Takes effect for the next SDK modal that is opened.


signIn(provider:)

func signIn(provider: SDKSignInProvider = .all) async throws -> AuthResult

Launches the OAuth sign-in flow. If provider is .all, shows the Apple/Google selection sheet first. After login, automatically checks cloud backup status and wallet state.

  • AuthResult.needsMigration == true means createWallet() will trigger the migration flow automatically.
ParameterDefaultDescription
provider.allOAuth provider to use

Returns AuthResult with cloudBackupStatus and needsMigration set.


signInAgain()

func signInAgain() async throws -> AuthResult

Re-authenticates using the stored provider. If the obtained account differs from the stored session, throws CROSSxError.accountMismatch without overwriting the session.

Returns AuthResult after re-authentication.

Throws CROSSxError.accountMismatch if the re-authenticated account does not match the stored session.


signInWithCreate(provider:)

func signInWithCreate(provider: SDKSignInProvider = .all) async throws -> AuthResult

Calls signIn(provider:) then createWallet() automatically in one step. If the wallet already exists, returns immediately without creating another. Returns with walletAddress set.

ParameterDefaultDescription
provider.allOAuth provider to use

Returns AuthResult with walletAddress set.


signInWithJWT(idToken:providerSub:provider:)

func signInWithJWT(
    idToken: String,
    providerSub: String? = nil,
    provider: String? = nil
) async throws -> AuthResult

Injects an externally obtained Firebase ID token directly into the SDK session, bypassing the OAuth flow. The backend exchanges the token via /social/login. All fields are stored identically to a normal signIn(), so getUserInfo() returns complete data.

ParameterDefaultDescription
idTokenFirebase ID token
providerSubnilOAuth provider's unique user ID (Google/Apple sub). Extracted from idToken if omitted
providernilProvider identifier (e.g. "google", "apple"). Extracted from idToken if omitted

Returns AuthResult with cloudBackupStatus and needsMigration set.

Throws CROSSxError.authFailed if backend token exchange or verification fails.


signOut()

func signOut() async throws

Ends the current session and clears all local data, including the cached wallet password.


refreshToken()

func refreshToken() async throws -> String

Returns a valid access token; refreshes via the stored refresh token if the current token is expired or close to expiry.

Returns a valid access token as a String.

Throws CROSSxError.sessionExpired if there is no token, refresh fails, or the session does not exist.


isLoggedIn()

func isLoggedIn() -> Bool

Returns true if a stored access token exists and has not expired. This is a synchronous, local check — no network call is made.


ensureLoggedIn()

func ensureLoggedIn() async -> Bool

Ensures the current session is usable. If the access token is already valid, returns true immediately. Otherwise attempts a token refresh.

Returns Booleantrue if a valid session is available, false if the session cannot be restored.


getUserInfo()

func getUserInfo() async throws -> SDKUserInfo

Builds SDKUserInfo from the JWT payload, stored OAuth tokens, and wallet address list.

Returns SDKUserInfo with provider info, tokens, email, sub, and wallet addresses.


Wallet Password and Biometrics

The SDK may prompt for a PIN (wallet password) before signing and sending. The PIN is cached after first entry and reused.

canUseBiometric()

func canUseBiometric() -> Bool

Returns true if biometric hardware (Face ID / Touch ID) is available and enrolled on the device.


isBiometricEnabled()

func isBiometricEnabled() -> Bool

Returns true if the stored wallet PIN is currently protected by Face ID / Touch ID.


setBiometricEnabled(_:)

func setBiometricEnabled(_ enabled: Bool) async throws

Enables or disables biometric protection for the cached wallet PIN. If no PIN is cached, shows the Enter PIN modal first.

ParameterDescription
enabledtrue to enable Face ID / Touch ID protection, false to disable

Throws CROSSxError.biometricFailed if biometric recognition fails.

Throws CROSSxError.userReject if the user cancels the biometric prompt or PIN modal.


Wallet Password Management

These methods accept the password directly — no PIN modal is shown. Collect the PIN from your own UI before calling.

verifyPassword(_:)

func verifyPassword(_ password: String) async throws -> Bool

Calls POST /mnemonic/verify-password to verify the wallet password against the server. The password is not cached.

Returns true if the password is correct.

Throws on network error or authentication failure.


changePassword(password:newPassword:)

func changePassword(password: String, newPassword: String) async throws -> ChangePasswordResponse

Verifies the current password and replaces it with the new one via POST /mnemonic/change-password.

ParameterDescription
passwordCurrent wallet password
newPasswordNew wallet password

Throws CROSSxError.passwordWrong if the current password does not match.


exportMnemonic(password:)

func exportMnemonic(password: String) async throws -> GetMnemonicResponse

Returns the wallet mnemonic phrase via POST /mnemonic/mnemonic. No UI is shown — pass the password collected from your own UI.

Throws CROSSxError.passwordWrong if the password does not match.


exportPrivateKey(password:index:)

func exportPrivateKey(password: String, index: Int = 0) async throws -> GetPrivateKeyResponse

Returns the private key for the given derivation index via POST /mnemonic/private-key. No UI is shown — pass the password collected from your own UI.

ParameterDefaultDescription
passwordWallet password collected from your UI
index0HD wallet derivation index

Throws CROSSxError.passwordWrong if the password does not match.


Address / Wallet

checkWallet()

func checkWallet() async throws -> WalletCheckStatus

Queries the server for the current wallet state without creating or modifying anything. Requires a valid access token but does not require authentication.

Returns WalletCheckStatus.exists | .migrationRequired | .notFound.


createWallet(migrateAutomatically:)

func createWallet(migrateAutomatically: Bool = true) async throws -> CreateWalletResponse

Creates a new wallet or migrates an existing one, with UI for PIN/password entry.

ParameterDefaultDescription
migrateAutomaticallytrueWhen .migrationRequired: true shows migration UI, false throws CROSSxError.migrationRequired

Branches:

  • .exists — prompts for the existing PIN if not cached; returns the current address.
  • .migrationRequired + migrateAutomatically = true — shows Wallet Found → PIN migration UI.
  • .migrationRequired + migrateAutomatically = false — throws CROSSxError.migrationRequired.
  • .notFound — creates a new wallet and prompts for a new PIN.

Returns CreateWalletResponse with the resolved address.


getAddress(index:)

func getAddress(index: Int = 0) async throws -> GetAddressResponse

Returns the wallet address at the given index. Prompts for PIN via Enter PIN modal if the password is not cached.

ParameterDefaultDescription
index0HD wallet derivation index

Returns GetAddressResponseaddress and index.


getAddresses()

func getAddresses() async throws -> GetAddressesResponse

Returns all cached wallet addresses. Returns an empty array if no addresses are cached. Each address includes the name label set by setAddressNames(_:).

Returns GetAddressesResponseaddresses: [WalletAddressInfo].


selectWallet(currentAddress:)

func selectWallet(currentAddress: String? = nil) async throws -> WalletAddressInfo?

Presents an HD wallet selector modal. Tapping "add a wallet" derives the next HD wallet index automatically.

ParameterDefaultDescription
currentAddressnilAddress to highlight with a "selected" badge

Returns WalletAddressInfo? — the selected wallet, or nil if the user dismisses the modal.

Throws CROSSxError.sessionExpired if not logged in.


recoverWallet(shareC:)

func recoverWallet(shareC: String) async throws -> RecoverWalletResponse

Recovers a wallet using a cloud backup share.

Returns RecoverWalletResponse — resolved address.


Chain Management

Chain management APIs do not require authentication. They only check the project whitelist.

getChains()

func getChains() async throws -> [ChainInfo]

Returns all chains registered for the project, each with their chainId and rpcUrl.

Returns [ChainInfo].


getChain(chainId:)

func getChain(chainId: String) async throws -> ChainInfo

Returns the chain info for the given CAIP-2 chainId.

Returns ChainInfochainId and rpcUrl.

Throws CROSSxError.unsupportedChain if the chain is not registered for the project.


getRpcUrl(for:)

func getRpcUrl(for chainId: String) async throws -> String

Returns the RPC URL for the given CAIP-2 chainId.

Returns String — the RPC URL.

Throws CROSSxError.unsupportedChain if the chain is not registered for the project.


Signing / Sending

All signing and sending methods require chainId. Use CAIP-2 eip155:<number> format (e.g. eip155:1, eip155:612044).

If dappName is nil, SDKConfig.appName is used.

Auto gas resolution for sendTransaction / sendTransactionAndWait

If nonce, gasLimit, or gas price fields are omitted from UnsignedTransaction, the SDK resolves them automatically via RPC before broadcasting:

  • nonceeth_getTransactionCount (pending)
  • gasLimiteth_estimateGas
  • Gas price → EIP-1559 (baseFeePerGas + 1 Gwei) if supported, otherwise legacy 2 Gwei fallback

signMessage(_:chainId:from:dappName:accountName:)

func signMessage(
    _ message: String,
    chainId: String,
    from: String? = nil,
    dappName: String? = nil,
    accountName: String = "Account"
) async throws -> SignMessageResponse

Shows a confirmation dialog and signs a plain-text message (EIP-191) with the user's wallet.

ParameterDefaultDescription
messagePlain text message to sign
chainIdCAIP-2 chain identifier (e.g. eip155:1)
fromnilSigning address
dappNamenilDApp display name shown in the UI (falls back to SDKConfig.appName)
accountName"Account"Account label shown in the UI

Returns SignMessageResponsesignature: String?.


signTypedData(_:chainId:from:dappName:accountName:) — JSON string

func signTypedData(
    _ typedDataJson: String,
    chainId: String,
    from: String? = nil,
    dappName: String? = nil,
    accountName: String = "Account"
) async throws -> SignTypedDataResponse

Shows a confirmation dialog and signs EIP-712 typed data supplied as a JSON string. Routes to POST /mnemonic/sign-typed-data/:chainId.

For on-chain typed data, use chainId = "eip155:<number>" and ensure typedData.domain.chainId exists and matches the numeric part. Server mismatch errors return code -10026 (domain chainId mismatch).

Returns SignTypedDataResponsesignature: String?.


signTypedData(_:chainId:from:dappName:accountName:) — JSON Data

func signTypedData(
    _ typedDataJSON: Data,
    chainId: String,
    from: String? = nil,
    dappName: String? = nil,
    accountName: String = "Account"
) async throws -> SignTypedDataResponse

Same as above, but accepts raw JSON Data instead of a String.


signTypedDataOffchain(_:from:dappName:accountName:)

// JSON string overload
func signTypedDataOffchain(
    _ typedDataJson: String,
    from: String? = nil,
    dappName: String? = nil,
    accountName: String = "Account"
) async throws -> SignTypedDataResponse

// JSON Data overload
func signTypedDataOffchain(
    _ typedDataJSON: Data,
    from: String? = nil,
    dappName: String? = nil,
    accountName: String = "Account"
) async throws -> SignTypedDataResponse

Signs EIP-712 typed data for off-chain use cases where domain.chainId is absent. Internally sets chainId = "0". Use this instead of signTypedData(_:chainId:...) when the payload omits domain.chainId.

Returns SignTypedDataResponsesignature: String?.


signTransaction(_:chainId:dappName:networkName:estimatedFee:amount:)

func signTransaction(
    _ unsignedTx: UnsignedTransaction,
    chainId: String,
    dappName: String? = nil,
    networkName: String? = nil,
    estimatedFee: String? = nil,
    amount: String? = nil
) async throws -> SignTransactionResponse

Shows a confirmation dialog and signs a transaction. The signed transaction is not broadcast — use sendTransaction to sign and broadcast in one step.

ParameterDefaultDescription
networkNamenilHuman-readable network name shown in the confirmation UI
estimatedFeenilFee string shown in the confirmation UI
amountnilAmount string shown in the confirmation UI

Returns SignTransactionResponsesignedTx: String?, txHash: String?.


sendTransaction(_:chainId:dappName:networkName:estimatedFee:amount:)

func sendTransaction(
    _ unsignedTx: UnsignedTransaction,
    chainId: String,
    dappName: String? = nil,
    networkName: String? = nil,
    estimatedFee: String? = nil,
    amount: String? = nil
) async throws -> SendTransactionResponse

Shows a confirmation dialog, signs, and broadcasts the transaction to the network. Missing gas fields are resolved automatically via RPC.

Returns SendTransactionResponsetxHash: String?.


sendTransactionAndWait(_:chainId:dappName:networkName:estimatedFee:amount:options:)

func sendTransactionAndWait(
    _ unsignedTx: UnsignedTransaction,
    chainId: String,
    dappName: String? = nil,
    networkName: String? = nil,
    estimatedFee: String? = nil,
    amount: String? = nil,
    options: PollingOptions = PollingOptions()
) async throws -> SendTransactionAndWaitResponse

Same as sendTransaction but polls for the on-chain receipt before returning. Displays "Transaction complete" or "Transaction reverted" in the confirmation UI based on the receipt status.

ParameterDefaultDescription
optionsPollingOptions()Polling interval and timeout (default: 2s interval, 60s timeout)

Returns SendTransactionAndWaitResponsetxHash: String, receipt: TransactionReceipt.


sendTransactionWithWaitForReceipt(_:chainId:dappName:networkName:estimatedFee:amount:timeoutMs:pollIntervalMs:)

func sendTransactionWithWaitForReceipt(
    _ unsignedTx: UnsignedTransaction,
    chainId: String,
    dappName: String? = nil,
    networkName: String? = nil,
    estimatedFee: String? = nil,
    amount: String? = nil,
    timeoutMs: Int = 30_000,
    pollIntervalMs: Int = 1_000
) async throws -> TransactionReceipt

Android-compatible overload of sendTransactionAndWait. Returns TransactionReceipt directly.

ParameterDefaultDescription
timeoutMs30_000Maximum time to wait for receipt (ms)
pollIntervalMs1_000Polling interval (ms)

Returns TransactionReceipt.


RPC / Helpers

Direct RPC calls route through the chain's RPC URL (resolved via getChain). Use CAIP-2 eip155:<number> format for chainId.

walletRpc(request:chainId:)

func walletRpc(request: JsonRpcRequest, chainId: String) async throws -> String

Generic JSON-RPC request sent directly to the chain's RPC node. Use for reads and calls (e.g. eth_call). For signing and broadcasting, use the dedicated APIs.

Returns JSON-RPC result as a String (hex values, or JSON string for object results).


getBalance(address:chainId:blockTag:)

func getBalance(address: String, chainId: String, blockTag: String = "latest") async throws -> String

Calls eth_getBalance(address, blockTag) against the chain's RPC URL.

ParameterDefaultDescription
addressEVM wallet address
chainIdCAIP-2 chain identifier
blockTag"latest"Block tag ("latest", "pending", etc.)

Returns hex string balance in wei, e.g. "0xde0b6b3a7640000".


getTokenBalance(contractAddress:ownerAddress:chainId:blockTag:)

func getTokenBalance(
    contractAddress: String,
    ownerAddress: String,
    chainId: String,
    blockTag: String = "latest"
) async throws -> String

Calls eth_call with the ERC-20 balanceOf selector to retrieve an ERC-20 token balance.

ParameterDefaultDescription
contractAddressERC-20 token contract address
ownerAddressWallet address to query the balance for
chainIdCAIP-2 chain identifier
blockTag"latest"Block tag

Returns hex string balance in the token's smallest unit.


getNonce(address:chainId:blockTag:)

func getNonce(address: String, chainId: String, blockTag: String = "pending") async throws -> String

Calls eth_getTransactionCount(address, blockTag). Use "pending" (default) to include pending transactions — recommended before signing or sending.

ParameterDefaultDescription
addressEVM wallet address
chainIdCAIP-2 chain identifier
blockTag"pending"Block tag ("pending" recommended for pre-sign nonce)

Returns hex string nonce, e.g. "0x5".


getGasPrice(chainId:)

func getGasPrice(chainId: String) async throws -> String

Calls eth_gasPrice against the chain's RPC URL.

Returns hex string gas price in wei, e.g. "0x3B9ACA00".


getMaxPriorityFeePerGas(chainId:)

func getMaxPriorityFeePerGas(chainId: String) async throws -> String

Calls eth_maxPriorityFeePerGas against the chain's RPC URL.

Returns hex string priority fee in wei.


getBaseFeePerGas(chainId:)

func getBaseFeePerGas(chainId: String) async throws -> String?

Fetches the latest block header and extracts baseFeePerGas. Returns nil for legacy chains that do not support EIP-1559.

Returns hex string base fee in wei, or nil for legacy chains.


estimateGas(_:chainId:)

func estimateGas(_ unsignedTx: UnsignedTransaction, chainId: String) async throws -> String

Calls eth_estimateGas for the given transaction.

Returns hex string gas limit estimate, e.g. "0x5208".


getTransactionReceipt(txHash:chainId:)

func getTransactionReceipt(txHash: String, chainId: String) async throws -> TransactionReceipt?

Calls eth_getTransactionReceipt for the given transaction hash.

Returns TransactionReceipt?nil if the transaction is still pending.


waitForTransaction(txHash:chainId:options:)

func waitForTransaction(
    txHash: String,
    chainId: String,
    options: PollingOptions = PollingOptions()
) async throws -> TransactionReceipt

Polls for a transaction receipt until confirmed or timed out.

ParameterDefaultDescription
txHashTransaction hash to wait for
chainIdCAIP-2 chain identifier
optionsPollingOptions()Polling interval and timeout (default: 2s interval, 60s timeout)

Returns TransactionReceipt.


waitForTxAndGetReceipt(txHash:chainId:timeoutMs:pollIntervalMs:)

func waitForTxAndGetReceipt(
    txHash: String,
    chainId: String,
    timeoutMs: Int = 30_000,
    pollIntervalMs: Int = 1_000
) async throws -> TransactionReceipt

Android-compatible overload of waitForTransaction.

ParameterDefaultDescription
txHashTransaction hash to wait for
chainIdCAIP-2 chain identifier
timeoutMs30_000Maximum wait time (ms)
pollIntervalMs1_000Polling interval (ms)

Returns TransactionReceipt.


Types

UnsignedTransaction

FieldTypeDescription
chainIdString?CAIP-2 chain identifier
fromString?Sender address
toString?Recipient address
valueString?ETH value (hex string)
dataString?Call data (hex string)
nonceString?Transaction nonce (hex string); auto-resolved if omitted
gasLimitString?Gas limit (hex string); auto-resolved if omitted
gasPriceString?Gas price for legacy transactions (hex string)
maxFeePerGasString?EIP-1559 max fee (hex string)
maxPriorityFeePerGasString?EIP-1559 priority fee (hex string)

PollingOptions

FieldDefaultDescription
intervalMs2000Polling interval in milliseconds
timeoutMs60_000Maximum polling duration in milliseconds

JsonRpcRequest

public struct JsonRpcRequest: Codable, Sendable {
    public let jsonrpc: String  // always "2.0"
    public let id: Int          // default 1
    public let method: String
    public let params: [JsonRpcParam]

    // Convenience init for simple string params
    public init(id: Int = 1, method: String, params: [String] = [])

    // Mixed params init (for eth_call etc.)
    public init(id: Int = 1, method: String, params: [JsonRpcParam])
}

public enum JsonRpcParam: Sendable, Codable {
    case string(String)
    case bool(Bool)
    case object([String: String])
}

JsonRpcResponse

public struct JsonRpcResponse: Sendable, Codable {
    public let jsonrpc: String
    public let id: Int
    public let error: JsonRpcError?
    public let resultData: Data?        // raw JSON bytes of result field

    public var result: String?          // result as String (e.g. hex values)
    public var isResultNull: Bool       // true if result is null (pending receipt etc.)
    public func decodeResult<T: Decodable>(_ type: T.Type) throws -> T?
}

JsonRpcError

FieldDescription
codeJSON-RPC error code
messageError message

TransactionReceipt

public struct TransactionReceipt: Sendable, Decodable {
    public let transactionHash: String
    public let blockHash: String
    public let blockNumber: String
    public let from: String
    public let to: String?              // nil for contract creation
    public let gasUsed: String
    public let effectiveGasPrice: String
    public let status: String           // "0x1" = success, "0x0" = reverted
    public let transactionIndex: String
    public let type: String
}

AuthResult

public struct AuthResult: Sendable {
    public let success: Bool
    public let walletAddress: String?
    public let user: UserInfo?
    public let cloudBackupStatus: CloudBackupStatus?
    public let needsMigration: Bool?
}

CloudBackupStatus

public struct CloudBackupStatus: Sendable {
    public let hasBackup: Bool          // true if a legacy native app backup exists
    public let primaryData: PrimaryStoreData?
    public let nonce: Int?
}

If hasBackup is true, the user has a legacy wallet backup recoverable via the createWallet() migration flow.

WalletCheckStatus

public enum WalletCheckStatus: String, Codable, Sendable {
    case exists = "exists"
    case migrationRequired = "migration_required"
    case notFound = "not_found"
}

UserInfo

FieldDescription
idInternal user ID
emailUser email

SDKUserInfo

public struct SDKUserInfo: Sendable {
    public let status: String           // "success"
    public let data: SDKUserInfoData
    public let loginType: String?       // "google" or "apple"
    public let addresses: [WalletAddressInfo]

    // Convenience accessors (delegated from data)
    public var id: String               // data.sub
    public var email: String?           // data.email
    public var provider: String?        // data.provider
    public var accessToken: String      // data.accessToken
    public var idToken: String?         // data.idToken
    public var sub: String              // data.sub
    public var providerSub: String?     // data.providerSub
}

SDKUserInfoData

public struct SDKUserInfoData: Sendable {
    public let provider: String?        // "google" or "apple"
    public let accessToken: String
    public let idToken: String?
    public let email: String?
    public let sub: String              // JWT subject (user ID)
    public let providerSub: String?     // OAuth provider's original sub
}

WalletAddressInfo

FieldDescription
addressWallet address
indexHD wallet derivation index

ChainInfo

public struct ChainInfo: Codable, Sendable, Equatable {
    public let chainId: String   // CAIP-2 format, e.g. "eip155:612044"
    public let rpcUrl: String
}

RecoverWalletResponse

FieldDescription
addressRecovered wallet address

SignMessageResponse

FieldDescription
signatureHex signature string

SignTypedDataResponse

FieldDescription
signatureHex signature string

SignTransactionResponse

FieldDescription
signedTxSigned transaction hex string
txHashTransaction hash

SendTransactionResponse

FieldDescription
txHashBroadcast transaction hash

SendTransactionAndWaitResponse

FieldDescription
txHashTransaction hash
receiptConfirmed TransactionReceipt

Errors

All SDK errors are cases of CROSSxError.

Auth errors

ErrorDescription
notAuthenticatedNo active session; user must sign in
authFailedAuthentication failed (backend rejection or invalid token)
tokenExpiredAccess token is expired
sessionExpiredAccess token unusable and refresh failed. Requires sign-out
accountMismatchsignInAgain() obtained a different account than the stored session

User action

ErrorDescription
userRejectedUser dismissed a confirmation or wallet modal
userRejectUser cancelled a biometric or PIN prompt

Wallet errors

ErrorDescription
migrationRequiredWallet requires migration and migrateAutomatically = false
migrationPinLockedToo many wrong migration PINs; check associated PinLockInfo
walletInconsistentStateInternal wallet state is inconsistent
passwordWrongWrong wallet password entered
passwordLockedToo many wrong password attempts; check associated PinLockInfo
biometricFailedBiometric recognition failed (iOS)

Sign / send errors

ErrorDescription
signFailedSigning operation failed
transactionFailedTransaction was rejected or reverted

Chain errors

ErrorDescription
unsupportedChainChain is not registered for the project
invalidChainIdProvided chainId is malformed

Other errors

ErrorDescription
networkErrorNetwork request failed
invalidConfigSDK configuration is invalid
configurationMissingRequired Info.plist key is missing
unknownUnexpected error

Message strings may vary with SDK resources and locale.

Wallet gateway error codes

CodeDescription
-10026domain chainId mismatchdomain.chainId in the payload does not match the request chainId