Kotlin/Java
The Colibri bindings for Kotlin/Java are built using CMake and Gradle. It can be used as AAR (Android Archive) or JAR (Java Archive).
💡 Quick Start: Check out the Example Android App for a complete working implementation!
Installation
The Colibri Kotlin/Java bindings are published to GitHub Packages and are publicly available without authentication.
Adding the Repository
Add the GitHub Packages repository to your project:
Groovy (build.gradle):
repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/corpus-core/colibri-stateless")
}
}Kotlin DSL (build.gradle.kts):
repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/corpus-core/colibri-stateless")
}
}Note: The packages are public and no authentication is required for downloading.
Versioning
The packages are automatically published with semantic versioning:
Release versions (e.g.,
1.0.0,1.2.3): Created from Git tags likev1.0.0Snapshot versions (e.g.,
1.0.0-SNAPSHOT): Built from thedevbranch on every push
For production use, always pin to a specific release version. Use SNAPSHOT versions only for development and testing.
Usage
Java (JAR)
Add the JAR dependency to your build.gradle file:
Use it like this:
Kotlin
For Android (AAR)
For Android projects, use the AAR artifact that includes native libraries for all Android ABIs (armeabi-v7a, arm64-v8a, x86, x86_64):
For JVM/Server (JAR)
For server-side Kotlin or JVM projects, use the JAR artifact that includes native libraries for Linux, macOS (ARM64), and Windows:
Use it like this:
Configuration
Prover Mode
Controls how proofs are built and verified. Set via proverMode in the constructor:
ProverMode.LOCAL-- Proofs are built entirely on the client. Requires access to a Beacon API and execution layer RPC. Fully trustless, but slower and needs more infrastructure.ProverMode.REMOTE-- Proofs are fetched from a remote Colibri prover server. Fastest option but relies on the prover server for proof generation. The verifier still cryptographically checks every proof.ProverMode.HYBRID-- The consensus-layer proof (BlockHeaderProof) comes from the Colibri server, while execution-layer data (account proofs, storage, etc.) is fetched directly from the RPC provider. Best balance of performance and scalability -- the Colibri server only serves lightweight, cacheable header proofs while the heavy RPC load goes to your existing provider.ProverMode.PROXY-- Like remote, but the client sends its own RPC and Beacon API URLs to the prover server. The server uses these endpoints instead of its own. Useful when the client has access to private or premium RPC providers.ProverMode.LIGHT_CLIENT-- Like hybrid, with additional background polling of block headers to keep the cache warm. CallstartLightClient()/stopLightClient()to control polling (default interval: 12s). By default only the compacteth_getBlockHeaderis fetched; passfullBlock = trueto fetch the full block (useful when manyeth_getTransactionByHash/eth_getTransactionReceiptcalls follow).
Default: ProverMode.REMOTE when prover URLs are configured, ProverMode.LOCAL otherwise.
Privacy (PAP)
PAP (Pragmatic Adaptive Privacy) reduces intent leakage towards RPC/prover by using cached data when available and verifying afterwards.
privacyMode–PrivacyMode.NONE(default) orPrivacyMode.BASIC. WithBASIC, the verifier sets the PAP flag so that method-type and verification can use cached storage for optimistic execution (e.g. foreth_call); method type may depend on params.
This feature is still experimental!
Weak Subjectivity Period check
Whenever a sync crosses the Weak Subjectivity Period (WSP) -- typically ~2 to 4 months on Ethereum mainnet -- the verifier anchors the highest finalized header against an external checkpointz / Beacon API endpoint. The check applies to all three sync paths: verifier-driven Light Client updates, prover-supplied LCSyncData, and prover-supplied ZKSyncData. For ZKSyncData the verifier prefers configured witness signatures (checkpointWitnessKeys + matching signatures from the prover) and only falls back to checkpointz when no witness anchor is available.
skipWspCheck(Boolean, defaultfalse) -- setsVERIFY_FLAG_SKIP_WSP_CHECK(bit1 shl 7) and disables the round-trip. SECURITY: only safe when another trust anchor (witness signatures, hard-coded checkpoint, signed package) is in place; raises the risk of long-range attacks across periods older than the WSP. See the threat model -- long range attacks for details.
Freshness window for latest proofs
latest proofsProofs that target the latest block tag remain cryptographically valid forever -- without a freshness window, a months-old proof could still be replayed as "current". The Kotlin binding therefore reads System.currentTimeMillis() / 1000 and forwards now - maxLatestAgeSeconds to the verifier, which rejects proofs whose block timestamp is older with "proof for latest too old".
The gate covers the following RPC methods:
EVM:
eth_call,eth_estimateGas,colibri_simulateTransactionAccount:
eth_getBalance,eth_getCode,eth_getStorageAt,eth_getTransactionCount,eth_getProofBlock / header:
eth_getBlockByNumber,eth_getBlockHeader,eth_blobBaseFee,eth_maxPriorityFeePerGasImplicit-latest:
eth_blockNumber
eth_getLogs is not covered yet (tracked in issue #128). Account methods rely on a slim timestamp leaf inside the state proof which is only emitted by prover version ≥ 1.1.27; against older provers the verifier fails closed ("cannot verify freshness of latest block without block context").
maxLatestAgeSeconds(Long, default60≈ 5 Ethereum slots) -- upper bound on the accepted age. Set to0to disable the check (e.g. when using legacy proof formats that do not embed a block context).
Caveat: the gate fires only on
"latest"(not"safe"/"finalized"). If the host wallclock is behindmaxLatestAgeSeconds(devices without configured time, fresh emulators), the lower bound clamps to0and the check is silently disabled. Make sure your runtime has a synced clock or setmaxLatestAgeSeconds = 0explicitly to acknowledge this state.
PAP mode: the freshness check also applies to PAP, where the call proof arrives via
colibri_proofCall(same proof structure as a directeth_call). This requires a prover that embeds the block context (≥ 1.1.15); against an older PAP proof without a block timestamp the check fails closed ("cannot verify freshness of latest block without block context"). SetmaxLatestAgeSeconds = 0Lto opt out.
Privacy-preserving eth_call (oblivious + PAP + hybrid)
eth_call (oblivious + PAP + hybrid)For an eth_call with full storage privacy, use hybrid prover mode, PAP, and oblivious nodes (obliviousNodes default is empty).
HYBRID: only the block proof from the prover; storage values from RPC/oblivious node, verified locally.
PAP (
PrivacyMode.BASIC): noeth_createAccessListon the prover; optimistic local EVM, onlyeth_getProofleaves the client.Oblivious: TEE RPC for
eth_getProof; enables OBLIVIOUS + PAP verify flags automatically. See Oblivious Labs for TEE/ORAM background.
Error handling
The binding throws ColibriException for any failure (proof, network, RPC, etc.). Verified EVM reverts are signalled by the dedicated subclass ColibriRevertException so callers can distinguish them from transport/proof errors.
Verified EVM reverts (ColibriRevertException)
ColibriRevertException)When an eth_call (or similar EVM execution) is verified successfully but the EVM itself executed a REVERT, the binding throws ColibriRevertException. This is a fully verified outcome -- not a transport or proof failure -- and matches the Geth-style RPC error { "code": 3, "message": "execution reverted", "data": "0x..." }.
ColibriRevertException extends ColibriException (with code = 3) and exposes the raw revert return data as a 0x-prefixed hex string in data. Callers typically ABI-decode this against the contract's error definitions (custom errors, Error(string), etc.). This is the mechanism that lets dApp libraries decode OffchainLookup (EIP-3668 / CCIP-Read) for example for the ENS off-chain resolver.
Example Android App
A complete working example is available in the example directory. This minimal Android app demonstrates:
Real-world usage: How to integrate Colibri in an Android application
RPC calls: Using
eth_blockNumberto fetch the current Ethereum block numberError handling: Proper exception handling for network and Colibri errors
Async operations: Using Kotlin coroutines for non-blocking RPC calls
UI integration: Updating Android UI components based on RPC results
Running the Example
The example app includes:
Simple UI with block number display and refresh button
Automatic block number fetching on startup
Error states and loading indicators
Public Ethereum RPC endpoint configuration (no API keys required)
Resources
📦 GitHub Packages - All published versions (JAR & AAR)
📖 Kotlin/Java Documentation - This complete documentation
🔗 Supported RPC Methods - Full list of available Ethereum RPC calls
🏗️ Building Guide - Build from source instructions
🧪 Example Android App - Complete working implementation
Building
Make sure you have the Java SDK, Cmake and Swig installed.
JAR
While the CI is building the native libs for multiple platforms, you can build the JAR locally with:
AAR
Of course you need to install the Android SDK and NDK first.
Last updated