MidnightSdk
Top-level entry point for the Midnight Android SDK.
Provides a fully standalone environment for dApps: contract execution, local proving, embedded wallet (dust fee payment), and node submission. No external wallet process (mn serve) required.
val sdk = MidnightSdk.Builder(context)
.network(MidnightNetwork.PREPROD)
.seed(mnemonicSeed)
.build()
val contract = MidnightContract.create(sdk.config) {
contractJs = assets.open("runtime/my-contract-iife.js")
address = contractAddress
coinPublicKey = sdk.coinPublicKey
// ... witnesses, private state
}
val receipt = contract.call("myCircuit", arg1) // Fully standalone
sdk.close()Types
Builder for MidnightSdk.
Coarse progress stages for sendNight, for driving a UI spinner/status.
Outcome of sendNight — a typed result so callers can render distinct UX.
Properties
HD derivation path of the access key (e.g., "m/44'/2400'/0'/5/0").
Access key for sigil identity — 33-byte compressed secp256k1 public key. Use this with KeyAuthorization to build delegation payloads.
Coin public key for circuit execution (32 bytes).
MidnightConfig pre-wired with the embedded wallet.
Registry of in-flight value-bearing operations (#261-264). Observe OperationRegistry.active / OperationRegistry.outcomes to project any UI (the wallet pill is just one consumer); enroll your own long-running work via runForegroundOperation / launchForegroundOperation to get the same process-survival + notification treatment the SDK's own send/dust/contract actions receive.
Proving key manager (for checking/downloading keys).
Shielded receive address (Bech32m-encoded, HRP mn_shield-addr_<network>).
Embedded wallet for direct balance/submit operations.
Unshielded wallet address (Bech32m-encoded).
Functions
Build the unshielded-funding JSON that lets a contract call fund the value a contract receives via receiveUnshielded (e.g. a treasury deposit). Pass the result to MidnightContract.call's unshieldedFundingJson; the native assembler adds + signs a guaranteed offer whose inputs cover amount (+ change back to this wallet).
Build the unshielded-withdrawal JSON for a contract call whose circuit SENDS value out via sendUnshielded (e.g. a treasury withdrawal). Pass the result to MidnightContract.call's unshieldedWithdrawalJson; the native assembler adds a fallible offer with the recipient output (no inputs, no signature — the contract provides the value). The recipientAddressHash MUST match the recipient the contract claims (verify.rs claimed_unshielded_spends).
One-time pre-flight: confirm the node's ledger is coherent with the client's bundled version, so a skew surfaces as a named warning rather than an opaque Custom error: N mid-submit (#16).
Release all resources. Suspends because wallet.close() serializes the dust-state close against an in-flight balance (see MidnightWallet.close) — a teardown that raced a running balance is exactly what threw "DustLocalState has been closed" on PreProd.
Ids of protocol sagas that started but haven't completed — candidates to resume on app start. The dApp re-launches each via runProtocol with the same id; the ledger- anchored doneWhens skip the already-done steps. (Saga definitions are code the dApp re-provides — only ids are persisted.)
Fire-and-forget runForegroundOperation on the SDK's lifecycle scope: returns a Job immediately, and the tracked work keeps running (under the foreground service) even after the caller leaves the screen. Failures are logged, not thrown.
Fire-and-forget runProtocol on the SDK's lifecycle scope (survives the caller); #253.
Fire-and-forget sendNight on the SDK's lifecycle scope so the transfer survives the caller leaving the app (#263). Returns immediately; the durable lifecycle (foreground service + finalization notification) is owned by the operation registry. onProgress is best-effort for an in-app live view while the caller is still around; onResult delivers the typed SendResult when done (also on the SDK scope, so it survives the caller — a dead caller's callback is simply a no-op).
Observe the chain's new-block stream (#255). Each emission is a freshly-produced BlockInfo (height, hash, timestamp, ledger params). This is the same stream that drives com.midnight.kuira.core.compact.MidnightContract.observeLedger; exposed here so a dApp can react to chain progress directly (e.g. a tip indicator, time-gating). Cold — collecting opens an indexer subscription; cancel the collection to close it.
Register this wallet's NIGHT key to generate dust against its public dust key.
Pull the user BACK to the current foreground operation with a heads-up alert (#264 inbound) — e.g. a protocol step that needs their input, or a counterparty's move. Fires an alerting notification that taps back to the operation's screen. title/body are YOUR strings (the SDK emits none). No-op outside a tracked operation; the host suppresses it when the app is already foreground, so you can call it unconditionally.
Run block as a tracked foreground operation (#261-264) and return its result.
Run a durable, idempotent multi-step protocol saga as ONE foreground operation (#253). Each ProtocolScope.step is skipped when its doneWhen is already true on-ledger, so re-running id (after process death, or on app resume) resumes from the first not-done step with no double-submit — the chain is the journal. The inner contract.calls coalesce under this op, so it reads as ONE operation + one finalization push. label drives the notification.
Send unshielded NIGHT from this wallet to toAddress (#240 — Send from the wallet pill).
Update the live progress stage of the foreground operation the CURRENT coroutine is running inside (see runForegroundOperation) — this drives the ongoing notification text and the status-bar chip ("Submitting commit…" → "Finalizing…"). stage is YOUR string (the SDK emits none). No-op when called outside a tracked operation.