Skip to main content

iOS API Reference

Complete reference of the public iOS SDK surface (iosSdk/Sources/InterstellarSdk/). All members live on InterstellarSdk: ObservableObject unless noted otherwise.

Android parity

Since the iOS SDK was published as a standalone package, its API is name-identical to the Android SDK — the same function names (getterRootAccount, getterCampaigns, extrinsicClientTransfer, …), only the signatures differ (Swift vs Kotlin). You can port an app from one platform to the other without renaming anything; see the differences table at the bottom.

Conventions
  • @Published state is mutated on the main thread only (the SDK hops internally) — bind it directly in SwiftUI.
  • Methods prefixed with prepare… arm a pinpad/VCA flow; getter… read state; extrinsic… submit on-chain actions.
  • Errors are not thrown in most paths: failures are logged ([InterstellarSdk]) and surfaced as nil/false. create, prepareCoinTransfer and prepareRecoveryVcaVouch are the notable throws exceptions.
  • import InterstellarSdk also re-exports the raw Rust API (the uniffi bindings + the renderer C FFI) — see Exports.

Observable state (@Published)​

MemberTypeDescription
coinBalances[SdkCoinBalance][cached] One entry per supported coin: raw atomic balance, USD value and deposit address (see below).
totalBalanceUsdString[cached] Total portfolio in USD, 2 decimals ("12.34").
vcaCount / nfcCountIntRecovery methods (VCA token / NFC) registered by this device.
recoveryThresholdIntConfigured recovery threshold (default 1).
recoveryVouchSubmittedBooltrue once this device has vouched to recover a lost account (Claim enabled after the ~10-block delay). Persisted. Gate the Claim button on this.
pendingCircuitsPackageCircuitsPackage?Non-null while a pinpad flow is armed and its circuits are ready to render.
pendingCampaignIdString?Campaign id of the armed airdrop flow (nil otherwise).
navigateToPinpadBooltrue when your app must show the pinpad now (pinpad page).
showTxFailedBooltrue only when a coin-transfer pad input was rejected. Never infer failure from a nil pinpad result alone.
airdropPinpadCompletedBoolSet when an airdrop registration/claim pad completed (the reference app uses it to navigate back).

Other public properties​

MemberTypeDescription
wsURL / nodeURL / ipfsAddrStringThe backend endpoints the SDK talks to (defaults = the local dev stack). Set them at create time.
workerCliPtrUInt64The raw pointer to the Rust worker client. Exposed for advanced/debug use — you normally never touch it.
pubKeyCompressedData (read-only)The SEC1-compressed public key (33 bytes) of the device key.

Value objects​

  • SdkCoinBalance(coinIdx: Int32, rawBalance: Int64, usdBalance: Double, address: String?) — Identifiable by coinIdx. SdkCoinBalance.supportedCoins holds the coin table: DOT (10 decimals, $3.79), SOL (9, $177.9), BTC (8, $113796), ETH (18, $4192). SdkCoinBalance.usdPrice(for:) returns the price of a coin index.
  • CoinBalanceBuilder(coinIdx:decimals:usdPrice:) — the coin table entry; convertToUsd(rawBalance:) converts atomic units to USD.
  • CoinType (enum CoinType: Int, CaseIterable, Identifiable) — .dot = 0, .sol = 1, .btc = 2, .eth = 3. The order is wire-compatible with the Rust core and with Android's CoinType — do not reorder. Each case exposes intIndex (Int32) and name.
  • CurrencyMath (enum, static helpers) — priceUsd(_:), divisor(for:), coinToUsd(_:coinAmount:), coinToAtomic(_:amount:), amountToUsd(_:amount:), atomicToBase(_:divisor:), totalBalanceInUsd(_:), computeTotalUsd(_:). The price table is hardcoded (demo-grade values).
  • WalletBalances (struct) — dot/sol/btc/eth in base units (Decimal), the iOS mirror of Android's WalletBalances.
  • TxAmount (uniffi) — amount spec: .fixed(amountInAtomicUnits: UInt64) in atomic units, or .drainAccount().
  • PendingTxResult (uniffi) — result of getterClientGetPendingTx(): .requiresVca(circuits: CircuitsPackage) or .autoSent(txHash: String).
  • CircuitsPackage(circuitsPackage: Data, txId: Data, nbMessageDigits: UInt32) (uniffi) — opaque VCA package; render via TxPinpadScreen, never inspect.
  • CampaignInfo / UserCampaign (uniffi records) — same fields as Android (campaignId, status, chain, amountPerClaim, claimLimit, totalFunds, registrationCount, claimCount, fundingAddress / campaignId, state).
  • MobileKeychainSigner — the P-256 (secp256r1) ECDSA device key stored in the iOS Keychain (tag interstellar_mobile_proxy_v1); publicKeySec1Compressed is the 33-byte SEC1-compressed public key. It is the counterpart of the (internal) Android Keystore signer.

Initialization & lifecycle​

APISignatureDescription
createstatic func create(onServerResetDetected: @escaping () async -> Void) async throws -> InterstellarSdk[network] Only entry point. Registers the device, handles server reset, warms balances. throws on Keychain/FFI init failure.
createstatic func create(wsURL: String, nodeURL: String, ipfsAddr: String, onServerResetDetected: @escaping () async -> Void) async throws -> InterstellarSdkSame, with endpoint overrides (dev/test).
checkServerResetfunc checkServerReset(onServerResetDetected: @escaping () async -> Void) async -> Bool[network] Server-reset detection for app resume. true = detected & handled (re-registered, balances refreshed).
resetKeyPairAndRegisterfunc resetKeyPairAndRegister() async[network] Delete the Keychain key, wipe cache, register a new identity. User-initiated only.
updateWorkerEndpointForTestfunc updateWorkerEndpointForTest(wsURL: String) throwsTest-only: retarget the worker cli mid-session (Rust update_worker_cli_endpoint).
The init? is public but the factory is the way in

InterstellarSdk.init?(wsURL:nodeURL:ipfsAddr:) is public (it returns nil when the Keychain is unavailable), but create is the supported entry point: it is the only one that performs the registration, the server-reset check and the first balance pull.

Root & coin accounts​

APISignatureDescription
getterRootAccountfunc getterRootAccount() async -> Data[cached → network] The 32-byte root account id (hex-cached in the UserDefaults suite gg.interstellar.sdk.preferences).
getterClientGetAccountfunc getterClientGetAccount(coinTypeIdx: Int32) -> String?[cached → network] Coin client deposit address (usable as-is for transfers/faucets). Refresh via updateBalance.
getterClientGetAccountWithPrefixfunc getterClientGetAccountWithPrefix(coinTypeIdx: Int32) -> String?[cached → network] Prefixed address form (e.g. SS58 for DOT) for display.

Balances​

APISignatureDescription
updateBalancesfunc updateBalances(onServerResetDetected: (() async -> Void)? = nil) async[network] Refresh all coins (balance + address + prefixed address), then republish coinBalances/totalBalanceUsd. Optional server-reset check first.
updateBalancefunc updateBalance(coinTypeIdx: Int32, onServerResetDetected: (() async -> Void)? = nil) async[network] Refresh a single coin.
getterClientGetBalanceInCoinfunc getterClientGetBalanceInCoin(coinTypeIdx: Int32) -> Int64 and func getterClientGetBalanceInCoin(coinType: CoinType) -> Int64[cached] Raw atomic balance (no network). Two overloads: by index or by CoinType.
getterClientGetBalanceInUsdfunc getterClientGetBalanceInUsd(coinType: CoinType) -> Double[cached] Balance converted to USD (2 decimals, hardcoded price table).
getterClientGetTotalBalanceInUsdfunc getterClientGetTotalBalanceInUsd() -> Double[cached] Sum over all coins.
convertToUsdfunc convertToUsd(coin: CoinType, amount: Double) -> DoubleDisplay helper: USD value of a user-entered amount (2 decimals).
convertCoinToAtomicfunc convertCoinToAtomic(coinType: CoinType, amount: Double) -> Int64Convert a display amount (e.g. 0.1 ETH) to atomic units (Wei).

coinBalances[i].usdBalance and totalBalanceUsd give the cached USD views directly.

Transactions (coin transfer)​

Supported end-to-end flow — all steps are [network]:

// 1. Prepare the transaction (note: TxAmount is in ATOMIC units on iOS).
let _ = await sdk.extrinsicClientInitializeTransaction(
recipient: "9WzDX…",
txAmount: .fixed(amountInAtomicUnits: 500_000_000), // 0.5 SOL
coinTypeIdx: 1)

// 2. Ask the SDK what to do next.
if let pending = await sdk.getterClientGetPendingTx() {
switch pending {
case .autoSent(let txHash):
// No VCA required for this policy — already broadcast.
break
case .requiresVca:
// Arm the pinpad in one call:
try await sdk.prepareCoinTransfer(coinTypeIdx: 1) // → navigateToPinpad = true
}
}
APISignatureDescription
extrinsicClientInitializeTransactionfunc extrinsicClientInitializeTransaction(recipient: String, txAmount: TxAmount, coinTypeIdx: Int32) async -> StringInitialize a transaction to recipient. Returns a tx hash (or "" on failure).
getterClientGetPendingTxfunc getterClientGetPendingTx() async -> PendingTxResult?Inspect the pending tx: .requiresVca / .autoSent. nil on failure (e.g. insufficient funds).
prepareCoinTransferfunc prepareCoinTransfer(coinTypeIdx: Int32) async throws -> String?High-level: runs the pending-tx dance for you. Throws if there is no pending tx. If the flow was .autoSent, returns the broadcasted tx hash directly; otherwise arms the pinpad (navigateToPinpad = true) and returns nil.
extrinsicClientTransferfunc extrinsicClientTransfer(inputDigits: Data, coinTypeIdx: Int32) -> StringLow-level: submit pad digits. You normally do not call this — CircuitSurfaceView (inside TxPinpadScreen) does it via processPinpadInputs.
processPinpadInputsfunc processPinpadInputs(inputs: Data) -> String?The single entry the pad uses for all flows (transfer, recovery vouch, airdrop, demo). Returns the new broadcasted hash only when a coin transfer was accepted; otherwise nil. Clears the pinpad state afterwards.
getterLatestBroadcastedTxHashfunc getterLatestBroadcastedTxHash() -> String?Latest broadcasted tx hash for this account (receipt display).
clearPinpadfunc clearPinpad()Reset pinpad state (navigateToPinpad=false, package/campaign cleared).
Reading the transfer result

processPinpadInputs returns nil on success for non-transfer flows (recovery vouch, airdrop, demo). The only reliable failure signal for a coin transfer is showTxFailed == true (set when the returned hash did not change). Mirror of Android's contract.

Keypairs & message signing​

APISignatureDescription
extrinsicClientAddKeypairfunc extrinsicClientAddKeypair(coinTypeIdx: Int32) asyncCreate a new coin keypair (automatic at registration).
extrinsicClientSignMessagefunc extrinsicClientSignMessage(message: Data, coinTypeIdx: Int32) -> String[network] Sign an arbitrary message (e.g. eth_sign). Does NOT use a VCA.
getterSignMessagefunc getterSignMessage() -> Data?Retrieve the signature produced above.

Recovery​

APISignatureDescription
recoverySetThresholdfunc recoverySetThreshold(_ threshold: Int32) async -> Bool[network] Set the recovery threshold. false = rejected by the chain (local value unchanged).
recoveryAddNfcfunc recoveryAddNfc(nfcSerialNumber: Data) async -> Bool[network] Register an NFC card as recovery method (increments nfcCount on success).
recoveryAddVcaTokenfunc recoveryAddVcaToken() async -> Data[network] Generate a VCA token file (Data, file recovery-token.interstellar.enc) — your app handles saving/sharing it (on Android the SDK shares via InterstellarFileSharer). Increments vcaCount.
importRecoveryTokenfunc importRecoveryToken(encryptedVca: Data) async -> CircuitsPackage?Decrypt an imported token file into a VCA package (nil on corrupt data).
prepareRecoveryVcaVouchfunc prepareRecoveryVcaVouch(fileBytes: Data) async throwsHigh-level restore: import the token and arm the vouch pinpad. Throws on an undecryptable token.
recoveryVouchVcafunc recoveryVouchVca(txId: Data, inputs: Data)[network] Complete the VCA-token vouch (pad digits normally delivered through processPinpadInputs). Sets recoveryVouchSubmitted = true.
recoveryVouchNfcfunc recoveryVouchNfc(nfcSerialNumber: Data) async -> Bool[network] Vouch with an NFC card. Sets recoveryVouchSubmitted = true on success.
recoveryClaimfunc recoveryClaim() async[network] Claim the recovered account after the ~10-block delay; clears cached accounts/root and zeroes balances until refresh.
Vouch → Claim flow

Recovering a lost account: the helping device (with its own or the imported token/NFC) vouches — recoveryVouchVca or recoveryVouchNfc — then the same device waits for the ~10-block delay (watch recoveryVouchSubmitted == true) and finally calls recoveryClaim().

Airdrop campaigns​

APISignatureDescription
getterCampaignsfunc getterCampaigns() async -> [String][network] All campaign ids.
getterCampaignInfofunc getterCampaignInfo(_ campaignId: String) async -> CampaignInfo?[network] Full campaign details.
getterUserCampaignsfunc getterUserCampaigns() async -> [UserCampaign][network] This account's registrations/states.
extrinsicCreateCampaignfunc extrinsicCreateCampaign(coinTypeIdx: Int32, amountPerClaim: Int64, claimLimit: Int32, freezePeriod: Int32, registrationPeriod: Int32, claimPeriod: Int32) async[network] Create a campaign (organizer flow). The funding address comes back in getterCampaignInfo.
extrinsicFundCampaignfunc extrinsicFundCampaign(campaignId: String) async[network] Fund & activate a campaign.
prepareAirdropRegistrationfunc prepareAirdropRegistration(campaignId: String) async[network] High-level: arm the registration VCA pinpad.
prepareAirdropClaimfunc prepareAirdropClaim(campaignId: String) async[network] High-level: arm the claim VCA pinpad.
extrinsicPrepareRegistration / extrinsicCompleteRegistrationfunc extrinsicPrepareRegistration(campaignId: String) / func extrinsicCompleteRegistration(campaignId: String, inputIndices: Data)Low-level 2-step registration (VCA #1 created, then validated by the pad digits). Prefer prepareAirdropRegistration.
extrinsicPrepareClaim / extrinsicClaimAirdropfunc extrinsicPrepareClaim(campaignId: String) / func extrinsicClaimAirdrop(campaignId: String, inputIndices: Data)Low-level 2-step claim (VCA #2 created, then validated). Prefer prepareAirdropClaim.
getterAirdropVcafunc getterAirdropVca(campaignId: String) -> CircuitsPackage?Fetch the campaign's VCA package directly.

On/off-ramp (MoonPay)​

APISignatureDescription
signMoonpayUrlfunc signMoonpayUrl(queryString: String) async -> String?[network] Server-signs a MoonPay query string so your app can build a trusted MoonPay URL.

Demo transaction (visual cryptography sandbox) — not part of the public API​

The SDK code still carries a createDemoTx / demoTxCheckInputs pair: it garbles a display circuits package for a free-form message so the VCA renderer can be exercised without a real transaction. These are internal sandbox helpers, not public API — they are not documented as a contract, they may change or disappear, and they are not covered by the parity promise with Android. Do not build on them; use the real flows (prepareCoinTransfer, prepareAirdropRegistration, prepareAirdropClaim, prepareRecoveryVcaVouch) instead.

The pinpad UI (shipped with the SDK)​

TypeDescription
TxPinpadScreen(circuits:sdk:onTxDone:)The SwiftUI pad: 12 digit slots + the message bar, rendered through the garbled-circuit surface. See the pinpad guide.
CircuitSurfaceViewThe UIView that hosts the Metal/WGPU renderer (configure(circuitsPackage:), start(), stop(), onClickPinpadDigit(_:)).
CircuitSurfaceViewRepresentableThe UIViewRepresentable wrapper used by TxPinpadScreen.

The raw Rust API is re-exported​

import InterstellarSdk also gives you the uniffi bindings and the renderer C API (the package re-exports the two FFI modules). Handy for advanced use — e.g. calling the renderer directly:

import InterstellarSdk   // gives you the uniffi globals + the renderer C functions

var msgWidth: UInt32 = 0
var msgHeight: UInt32 = 0
var padWidth: UInt32 = 0
var padHeight: UInt32 = 0
_ = get_dimensions(ctxPtr, &msgWidth, &msgHeight, &padWidth, &padHeight)

The Android SDK exposes the same Rust surface through its RustWrapper adapter.

Threading & guarantees​

  • Every @Published mutation is routed through the SDK's main-thread hop, so SwiftUI/Combine never sees an off-main write.
  • The FFI calls block; the SDK runs them off the main thread. await the async methods from a Task.
  • Failures are logged ([InterstellarSdk] …) and surfaced as nil/false — check the return values, not the console.

What is not public API​

A few more symbols ship in the package; they are internal and are not part of the contract — do not use them (and do not expect them to exist in a future release):

SymbolWhat it is
the _getter… / _extrinsic… methodsthe internal wrappers over the uniffi bindings (the public API of the same name calls them).
the rust… functions of Rust.swiftthe raw uniffi wrappers — the same reason they exist.
InterstellarSdk.init?(wsURL:nodeURL:ipfsAddr:)the failable initializer; create is the supported entry point.
SdkVcaFlowTypethe internal "which flow is armed" state (it is public, but it is a detail of the SDK's state machine).

The public Rust surface is re-exported by the package (see above) — but the supported entry point is the InterstellarSdk API documented on this page.

Differences from Android (quick map)​

AndroidiOSNote
StateFlow<T>@Published varKotlin flows vs Combine/SwiftUI.
BigDecimal (base units)Decimal (base units) / Double (USD)Swift has no BigDecimal; the SDK uses Decimal for the money math.
JniTxAmount.fixed(0.5) — display unitsTxAmount.fixed(amountInAtomicUnits:) — atomic units⚠️ convert before calling on iOS (sdk.convertCoinToAtomic(coin:amount:)).
JniPendingTxResult(status, …)PendingTxResult .requiresVca/.autoSentSwift is an enum.
WalletBalances(dot:, sol:, …)[SdkCoinBalance] keyed by coinIdxiOS addresses ride along in the same DTO.
The SDK shares recovery tokens via InterstellarFileSharerrecoveryAddVcaToken() returns DataYour iOS app owns save/share.
Endpoint override at build timecreate(wsURL:nodeURL:ipfsAddr:)iOS factory takes runtime overrides.
getterRootAccount(): ByteArraygetterRootAccount(): DataSame name, Swift type.