API Reference
iOS SDK API Reference
This page documents the public API surface of CROSSxSDK for iOS.
Configuration Types
SDKConfig
SDKConfigpublic struct SDKConfig: Sendable {
public let bundle: Bundle
public let projectId: String
public let appName: String
public let callbackScheme: String
public let httpNetwork: HTTPNetworkConfig?
public let theme: SDKThemeMode
public let themeTokens: SDKThemeTokens?
public let debug: Bool
public init(
bundle: Bundle = .main,
projectId: String,
appName: String,
callbackScheme: String? = nil,
httpNetwork: HTTPNetworkConfig? = nil,
theme: SDKThemeMode = .system,
themeTokens: SDKThemeTokens? = nil,
debug: Bool = true
)
public static func fromInfoPlist(
projectId: String,
appName: String,
bundle: Bundle = .main,
httpNetwork: HTTPNetworkConfig? = nil
) throws -> SDKConfig
public static func defaultScheme(for projectId: String) -> String
}public enum SDKSignInProvider: Equatable, Sendable {
case all // shows provider selection sheet
case google
case apple
}Choose the provider at runtime in signIn(provider:) / signInWithCreate(provider:). .all shows the provider selection sheet.
Theme
public enum SDKThemeMode: String, Sendable {
case light
case dark
case system
}
public struct SDKThemeTokens: Sendable {
public var light: SDKColorOverrides?
public var dark: SDKColorOverrides?
}SDKColorOverrides
SDKColorOverridespublic 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
HTTPNetworkConfigpublic struct HTTPNetworkConfig: Sendable {
public let timeout: TimeInterval // default 30
public let maxRetries: Int // default 3
}Core API
static let version: String— current SDK versionfunc initialize() async throws -> AuthResult?func applyTheme(themeMode: SDKThemeMode? = nil, themeTokens: SDKThemeTokens? = nil)@discardableResult func handleURL(_ url: URL) -> Boolfunc signIn(provider: SDKSignInProvider = .all) async throws -> AuthResultfunc signInAgain() async throws -> AuthResultfunc signInWithCreate(provider: SDKSignInProvider = .all) async throws -> AuthResultfunc signInWithJWT(accessToken: String, refreshToken: String? = nil) async throws -> AuthResultfunc signOut() async throwsfunc refreshToken() async throws -> Stringfunc isLoggedIn() -> Boolfunc ensureLoggedIn() async -> Boolfunc getUserInfo() async throws -> SDKUserInfo
Behavior summary
initialize()→AuthResult?(includescloudBackupStatusif session restored;nilif no restorable session)signIn()→AuthResult(includescloudBackupStatusandneedsMigrationafter login)signInAgain()— Re-authenticates using the stored provider. If the account differs from the stored session, throwsCROSSxError.accountMismatchwithout overwriting the session.signInWithCreate()— CallssignIn()thencreateWallet()automatically. Returns withwalletAddressset.signInWithJWT()— Injects an externally issued access token directly into the SDK session (no OAuth flow).refreshToken()— Returns a valid access token; refreshes via stored refresh token if needed. ThrowsCROSSxError.sessionExpiredif refresh fails.getUserInfo()→SDKUserInfo
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.
func canUseBiometric() -> Boolfunc isBiometricEnabled() -> Boolfunc setBiometricEnabled(_ enabled: Bool) async throws
Notes
canUseBiometric()checks device hardware and enrollment status.isBiometricEnabled()checks if the stored PIN is protected by Face ID / Touch ID.setBiometricEnabled(_:)re-saves the PIN with or without biometric protection. Shows Enter PIN modal if no PIN is cached.- Throws
CROSSxError.biometricFailedon recognition failure andCROSSxError.userRejecton cancellation.
Address / Wallet
func checkWallet() async throws -> WalletCheckStatusfunc createWallet(migrateAutomatically: Bool = true) async throws -> CreateWalletResponsefunc getAddress(index: Int = 0) async throws -> GetAddressResponsefunc getAddresses() async throws -> GetAddressesResponsefunc selectWallet(currentAddress: String? = nil) async throws -> WalletAddressInfo?func recoverWallet(shareC: String) async throws -> RecoverWalletResponse
createWallet() branches
.exists— Prompts for the existing PIN if not cached; returns the current address..migrationRequired— IfmigrateAutomatically == true, shows migration UI (Wallet Found → PIN). Iffalse, throwsCROSSxError.migrationRequired..notFound— Creates a new wallet and prompts for a new PIN.
Return notes
checkWallet()→WalletCheckStatus(.exists|.migrationRequired|.notFound)getAddress()→GetAddressResponse(address,index)getAddresses()→GetAddressesResponse(addresses: [WalletAddressInfo])createWallet()→CreateWalletResponse(address)selectWallet()→WalletAddressInfo?(nil if user dismisses modal)recoverWallet()→RecoverWalletResponse(address)
Notes
checkWallet()does not require authentication but needs a valid access token.selectWallet()shows an HD wallet selector modal. PassingcurrentAddresshighlights the active wallet with a "selected" badge.
Wallet password management
func verifyPassword(_ password: String) async throws -> Boolfunc changePassword(password: String, newPassword: String) async throws -> ChangePasswordResponsefunc exportMnemonic(password: String) async throws -> GetMnemonicResponsefunc exportPrivateKey(password: String, index: Int = 0) async throws -> GetPrivateKeyResponse
These methods accept the password directly — no PIN modal is shown. Collect the PIN from your own UI before calling. Throws CROSSxError.passwordWrong on mismatch.
Chain Management
func getChains() async throws -> [ChainInfo]func getChain(chainId: String) async throws -> ChainInfofunc getRpcUrl(for chainId: String) async throws -> String
Return notes
getChains()→[ChainInfo](all chains registered for the project)getChain(chainId:)→ChainInfo(chainId,rpcUrl)getRpcUrl(for:)→String(RPC URL for the given chainId)
Chain management APIs do not require authentication. They only check the project whitelist.
Signing / Sending (UI confirm flow)
All methods require chainId: String.
Use CAIP-2 eip155:<number> format for chainId (for example eip155:1, eip155:612044).
signMessage(...)message: String- params:
from,dappName,accountName
signTypedData(...)- Variant: JSON string input (
typedDataJson: String) - Variant: JSON Data input (
typedDataJSON: Data) - params:
from,dappName,accountName
- Variant: JSON string input (
signTypedDataOffchain(...)- Variant: JSON string input (
typedDataJson: String) - Variant: JSON Data input (
typedDataJSON: Data) - params:
from,dappName,accountName - No
chainIdparameter — signs off-chain (chainId is set to"0"internally)
- Variant: JSON string input (
signTransaction(...)unsignedTx: UnsignedTransaction- params:
dappName,networkName,estimatedFee,amount
sendTransaction(...)unsignedTx: UnsignedTransaction- params:
dappName,networkName,estimatedFee,amount
sendTransactionAndWait(...)unsignedTx: UnsignedTransaction- params:
dappName,networkName,estimatedFee,amount - polling controls:
options: PollingOptions
sendTransactionWithWaitForReceipt(...)— Android-compatible overload ofsendTransactionAndWait. ReturnsTransactionReceiptdirectly. Polling controls:timeoutMs: Int = 30_000,pollIntervalMs: Int = 1_000
Return notes
signMessage()→SignMessageResponse(signature: String?)signTypedData()→SignTypedDataResponse(signature: String?)signTypedDataOffchain()→SignTypedDataResponse(signature: String?)signTransaction()→SignTransactionResponse(signedTx: String?,txHash: String?)sendTransaction()→SendTransactionResponse(txHash: String?)sendTransactionAndWait()→SendTransactionAndWaitResponse(txHash: String,receipt: TransactionReceipt)sendTransactionWithWaitForReceipt()→TransactionReceipt
signTypedData notes
- Routes to
POST /mnemonic/sign-typed-data/:chainId. - For on-chain typed-data signing: use
chainId = "eip155:<number>"and ensuretypedData.domain.chainIdexists and matches the numeric part. - For off-chain signing (no domain chainId): use
signTypedDataOffchain()instead. - Server mismatch errors may return code
-10026(domain chainId mismatch).
Auto gas resolution for sendTransaction / sendTransactionAndWait
If nonce, gasLimit, or gas price fields are omitted from UnsignedTransaction, the SDK automatically resolves them via RPC before broadcasting:
nonce→eth_getTransactionCount(pending)gasLimit→eth_estimateGas- Gas price → EIP-1559 (
baseFeePerGas + 1 Gwei) if supported, otherwise legacy2 Gweifallback
RPC / Helpers
Direct RPC calls route through the chain's RPC URL (fetched via
getChain). Use CAIP-2eip155:<number>format forchainId(for exampleeip155:1).
func walletRpc(request: JsonRpcRequest, chainId: String) async throws -> Stringfunc getBalance(address: String, chainId: String, blockTag: String = "latest") async throws -> Stringfunc getTokenBalance(contractAddress: String, ownerAddress: String, chainId: String, blockTag: String = "latest") async throws -> Stringfunc getNonce(address: String, chainId: String, blockTag: String = "pending") async throws -> Stringfunc getGasPrice(chainId: String) async throws -> Stringfunc getMaxPriorityFeePerGas(chainId: String) async throws -> Stringfunc getBaseFeePerGas(chainId: String) async throws -> String?func estimateGas(_ unsignedTx: UnsignedTransaction, chainId: String) async throws -> Stringfunc getTransactionReceipt(txHash: String, chainId: String) async throws -> TransactionReceipt?func waitForTransaction(txHash: String, chainId: String, options: PollingOptions = PollingOptions()) async throws -> TransactionReceiptfunc waitForTxAndGetReceipt(txHash: String, chainId: String, timeoutMs: Int = 30_000, pollIntervalMs: Int = 1_000) async throws -> TransactionReceipt— Android-compatible overload ofwaitForTransaction
Return notes
walletRpc()→String(JSON-RPC result as string; use for reads and calls only)getBalance()→String(hex wei, e.g."0xde0b6b3a7640000")getTokenBalance()→String(hex token units)getNonce()→String(hex nonce, e.g."0x5")getGasPrice()→String(hex wei, e.g."0x3B9ACA00")getMaxPriorityFeePerGas()→String(hex wei)getBaseFeePerGas()→String?(hex wei,nilfor legacy chains)estimateGas()→String(hex gas limit, e.g."0x5208")getTransactionReceipt()→TransactionReceipt?(nil if transaction is still pending)waitForTransaction()/waitForTxAndGetReceipt()→TransactionReceipt
Key Object Details
UnsignedTransaction
chainId: String?from: String?to: String?value: String?data: String?nonce: String?gasLimit: String?gasPrice: String?maxFeePerGas: String?maxPriorityFeePerGas: String?
PollingOptions
intervalMs: Int(default2000)timeoutMs: Int(default60_000)
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
code: Intmessage: String
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
hasBackupistrue, the user has a legacy wallet backup that can be recovered viacreateWallet()migration flow.
WalletCheckStatus
public enum WalletCheckStatus: String, Codable, Sendable {
case exists = "exists"
case migrationRequired = "migration_required"
case notFound = "not_found"
}UserInfo
id: Stringemail: String?
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
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
address: Stringindex: Int
ChainInfo
public struct ChainInfo: Codable, Sendable, Equatable {
public let chainId: String // CAIP-2 format, e.g. "eip155:612044"
public let rpcUrl: String
}RecoverWalletResponse
address: String
SignMessageResponse
signature: String?
SignTypedDataResponse
signature: String?
SignTransactionResponse
signedTx: String?txHash: String?
SendTransactionResponse
txHash: String?
SendTransactionAndWaitResponse
txHash: Stringreceipt: TransactionReceipt
Errors
Common CROSSxError cases:
- Auth:
notAuthenticated,authFailed,tokenExpired,sessionExpired,accountMismatch - User cancel:
userRejected,userReject - Wallet:
migrationRequired,migrationPinLocked,walletInconsistentState,passwordWrong,passwordLocked,biometricFailed - Sign / send:
signFailed,transactionFailed - Chain:
unsupportedChain,invalidChainId - Other:
networkError,invalidConfig,configurationMissing,unknown
Notes:
sessionExpired— access token unusable and refresh failed. Requires sign-out.accountMismatch—signInAgain()obtained a different account than the stored session.userRejected— user dismissed a confirmation or wallet modal.userReject— user cancelled a biometric or PIN prompt.migrationPinLocked— too many wrong migration PINs; check associatedPinLockInfo.passwordLocked— too many wrong wallet passwords; check associatedPinLockInfo.
Message strings may vary with SDK resources and locale.
Updated 1 day ago