SDKsClient-side
For the complete documentation index, see llms.txt. This page is also available as markdown.

Android (Kotlin) SDK

Install and use the ZeroDev Omni SDK Kotlin binding for Android and the JVM.

The ZeroDev Kotlin SDK is part of the Omni SDK — a single Zig core shipped with first-class bindings for Kotlin, Swift, Go, Rust, Python, and C. The Android AAR bundles native libraries for arm64-v8a and x86_64; the JVM JAR extracts the right native at runtime. It supports smart-account creation, signing, sponsored transactions, and EIP-7702 delegation on any EVM chain.

The Kotlin binding is currently in alpha.

Install

In your module's build.gradle.kts:

dependencies {
    // Android (AAR with bundled native libraries)
    implementation("app.zerodev:zerodev-aa:0.0.1-alpha.6")

    // Desktop JVM (JAR; extracts native library at runtime)
    implementation("app.zerodev:zerodev-aa-jvm:0.0.1-alpha.6")
}

Requires Android minSdk 24 (or JDK 11+ for the JVM artifact).

Setup

import dev.zerodev.aa.*

Context.create(
    projectId,
    chainId = 11155111,                          // Sepolia
    gas = GasMiddleware.ZERODEV,
    paymaster = PaymasterMiddleware.ZERODEV
).use { ctx ->
    Signer.local(privateKey).use { signer ->
        ctx.newAccount(signer, KernelVersion.V3_3).use { account ->
            // ...
        }
    }
}

Signer.generate() is useful for demos when you don't need to reuse a key. It requires a working OS CSPRNG; on hardened sandboxes where getrandom(2) is blocked the call throws rather than producing a weak key, so production paths in those environments should pass a real key via Signer.local(privateKey). All handles implement AutoCloseable — use Kotlin's use { } to release native resources deterministically.

Example — sponsored UserOp

import dev.zerodev.aa.*

Context.create(projectId, chainId = 11155111,
    gas = GasMiddleware.ZERODEV,
    paymaster = PaymasterMiddleware.ZERODEV).use { ctx ->
    Signer.local(privateKey).use { signer ->
        ctx.newAccount(signer, KernelVersion.V3_3).use { account ->
            val recipient = Address.fromHex("0x...")

            val hash = account.sendUserOp(listOf(Call(target = recipient)))
            val receipt = account.waitForUserOperationReceipt(hash)

            println("Success: ${receipt.success}")
            println("Tx: ${receipt.transactionHash}")
        }
    }
}

Custom signers

Implement SignerImpl to integrate any signing backend (Privy, HSM, MPC, custom keystore):

val signer = Signer.custom(object : SignerImpl {
    override fun signHash(hash: ByteArray): ByteArray = TODO()
    override fun signMessage(msg: ByteArray): ByteArray = TODO()
    override fun signTypedDataHash(hash: ByteArray): ByteArray = TODO()
    override fun getAddress(): ByteArray = TODO()
})

EIP-7702 delegation

EIP-7702 lets an EOA delegate its code to a Kernel contract so its address is the smart-account address — no CREATE2, no init code, no index. The SDK signs an authorization tuple on the first UserOp and attaches it via the eip7702Auth field; subsequent ops skip the auth once delegation is installed on-chain.

Context.create(projectId, chainId = 11155111,
    gas = GasMiddleware.ZERODEV,
    paymaster = PaymasterMiddleware.ZERODEV).use { ctx ->
    Signer.generate().use { signer ->                    // fresh EOA
        ctx.newAccount7702(signer, KernelVersion.V3_3).use { account ->
            // account.getAddress() == signer's EOA address
            val hash = account.sendUserOp(listOf(Call(target = account.getAddress())))
            account.waitForUserOperationReceipt(hash)
        }
    }
}

Custom signers can override SignerImpl.signAuthorization (and set providesSignAuthorization = true) to sign EIP-7702 tuples natively. Otherwise the SDK falls back to hashing the tuple and calling signHash.

Next steps

On this page