
Async, high-performance client for EVM blockchains with minimal allocations, coroutine-based RPC, safe error handling, type-safe contract binding generation, multicall/batch support, ENS and signing utilities.
ethers-kt is an async, high-performance Kotlin Multiplatform library for interacting with EVM-based blockchains. It targets JVM, Android, iOS and macOS.
High Performance: Optimized types and code to minimize the number of allocations and copying.
Clean Abstractions: Intuitive, extensible, and easy to use.
Async: RPC calls are coroutine-based — send() suspends rather than blocking. On JVM and Android you also get
blocking (sendAwait) and CompletableFuture (sendAsync) variants as inherited members, so Java callers need no
wrapping.
Safe: RPC calls return an error object in case of failure, instead of throwing an exception.
Smart contract bindings:
Batch RPC calls:
Multicall3 contract.| Target | Notes |
|---|---|
jvm |
Java 11 bytecode |
android |
minSdk 24 |
iosArm64 |
device |
iosX64, iosSimulatorArm64
|
simulator |
macosArm64 |
Everything except ethers-abigen and ethers-signers-gcp is available on every target. Those two are JVM-only by
nature — the first is build-time code generation, the second wraps the Google Cloud KMS client.
All releases are published to Maven Central. Changelog of each release can be found under Releases.
It's recommended to define BOM platform dependency to ensure that ethers-kt artifacts are compatible with each other.
plugins {
id("io.kriptal.ethers.abigen-plugin") version "2.0.0"
}
// default values
ethersAbigen {
directorySource("src/main/abi")
outputDir = "generated/source/ethers/main/kotlin"
}
// Define a maven repository where the library is published
repositories {
mavenCentral()
// for snapshot versions, use the following repository
//maven { url = uri("https://central.sonatype.com/repository/maven-snapshots/") }
}
dependencies {
// Define a BOM and its version
implementation(platform("io.kriptal.ethers:ethers-bom:2.0.0"))
// Define any required artifacts without version
implementation("io.kriptal.ethers:ethers-abi")
implementation("io.kriptal.ethers:ethers-core")
implementation("io.kriptal.ethers:ethers-providers")
implementation("io.kriptal.ethers:ethers-signers")
}In a multiplatform project, add the artifacts to commonMain instead:
kotlin {
sourceSets {
commonMain.dependencies {
implementation(project.dependencies.platform("io.kriptal.ethers:ethers-bom:2.0.0"))
implementation("io.kriptal.ethers:ethers-abi")
implementation("io.kriptal.ethers:ethers-core")
implementation("io.kriptal.ethers:ethers-providers")
implementation("io.kriptal.ethers:ethers-signers")
}
}
}To interact with the chain, you need to create a Provider instance, which is the main entry point for all RPC calls.
// create a provider, using a websocket as underlying transport
val provider = Provider.builder("<WS_URL>").buildAwait().unwrap()
// query the latest block number
val startBlockNum = provider.getBlockNumber().sendAwait().unwrap()
println("Starting at block $startBlockNum")
// subscribe to new blocks, blocking the calling thread. Use "forEachAsync" to stream without blocking the caller.
provider.subscribeNewHeads().sendAwait().unwrap().forEach {
println("New Block: ${it.number}, ${it.number - startBlockNum} blocks since start")
}buildAwait and sendAwait block the calling thread and exist only on JVM and Android. From common code — or any
coroutine — use the suspending build() and send() instead:
val provider = Provider.builder("<WS_URL>").build().unwrap()
val startBlockNum = provider.getBlockNumber().send().unwrap()
println("Starting at block $startBlockNum")
// "forEach" consumes on the calling thread on every platform. Its non-blocking counterpart, "forEachAsync",
// is JVM/Android-only - from common code, run this on a dispatcher of your choosing.
provider.subscribeNewHeads().send().unwrap().forEach {
println("New Block: ${it.number}, ${it.number - startBlockNum} blocks since start")
}Code is structured into multiple modules, each categorized by its purpose. Below is a brief overview of each module. For a more in-depth explanation, please refer to the individual module's README.md.
abi: Provides ABI primitives with encoding/decoding logic for all types supported by the EVM.
abigen: Code for generating type-safe smart contract bindings from JSON-ABI files.
abigen-plugin: Gradle plugin for generating smart contract bindings during the build process.
core: Contains optimized base types.
crypto: Includes cryptographic utilities for signing and verifying ECDSA signatures on the secp256k1 curve.
ens: Full support for ENS names and avatars, with wildcard resolution and offchain resolution
via CCIP-Read. EnsResolver resolves explicitly with typed errors, while EnsMiddleware is a
Middleware layer that accepts an ENS name anywhere a call request is expected.
providers: Logic for interacting with JSON-RPC API using various transports (HTTP, WebSocket).
rlp: Handles the encoding and decoding of RLP.
signers: Code for transaction/message signing, allowing multiple signing key sources:
hardware wallet, mnemonic or raw private key.
signers-gcp: Signer backed by Google Cloud KMS. JVM-only.
logger: Lightweight logging facade used across the other modules.
We are happy to have you here! Opportunities to get involved with ethers-kt are open to everyone, no matter your level of expertise. Please check the CONTRIBUTING.md to get started.
Before submitting a PR make sure to format the code and run all checks using the following command:
./gradlew ktlintFormat checkcheck builds and tests the Apple targets too, and Kotlin/Native cannot cross-compile those, so the command above only
completes on a macOS host. On Linux or Windows, run the JVM and Android half and let CI cover the rest:
./gradlew jvmKotest :ethers-abigen-plugin:testFormatting is source-set scoped there as well — see the java-test job in
pull-request-checks.yml for the exact task list CI uses on Linux.
First, check if any of the README files under each module answers your question. If the answer is not there please don't open an issue. Instead, you can open a thread under Discussions.
This library has been made possible thanks to the inspiration provided by the following projects:
Proof of Work: 0x6b0f9ff6f53ec22d8d2d92b1beb193cdc523628951b5c81779fabce9f51db351
ethers-kt is an async, high-performance Kotlin Multiplatform library for interacting with EVM-based blockchains. It targets JVM, Android, iOS and macOS.
High Performance: Optimized types and code to minimize the number of allocations and copying.
Clean Abstractions: Intuitive, extensible, and easy to use.
Async: RPC calls are coroutine-based — send() suspends rather than blocking. On JVM and Android you also get
blocking (sendAwait) and CompletableFuture (sendAsync) variants as inherited members, so Java callers need no
wrapping.
Safe: RPC calls return an error object in case of failure, instead of throwing an exception.
Smart contract bindings:
Batch RPC calls:
Multicall3 contract.| Target | Notes |
|---|---|
jvm |
Java 11 bytecode |
android |
minSdk 24 |
iosArm64 |
device |
iosX64, iosSimulatorArm64
|
simulator |
macosArm64 |
Everything except ethers-abigen and ethers-signers-gcp is available on every target. Those two are JVM-only by
nature — the first is build-time code generation, the second wraps the Google Cloud KMS client.
All releases are published to Maven Central. Changelog of each release can be found under Releases.
It's recommended to define BOM platform dependency to ensure that ethers-kt artifacts are compatible with each other.
plugins {
id("io.kriptal.ethers.abigen-plugin") version "2.0.0"
}
// default values
ethersAbigen {
directorySource("src/main/abi")
outputDir = "generated/source/ethers/main/kotlin"
}
// Define a maven repository where the library is published
repositories {
mavenCentral()
// for snapshot versions, use the following repository
//maven { url = uri("https://central.sonatype.com/repository/maven-snapshots/") }
}
dependencies {
// Define a BOM and its version
implementation(platform("io.kriptal.ethers:ethers-bom:2.0.0"))
// Define any required artifacts without version
implementation("io.kriptal.ethers:ethers-abi")
implementation("io.kriptal.ethers:ethers-core")
implementation("io.kriptal.ethers:ethers-providers")
implementation("io.kriptal.ethers:ethers-signers")
}In a multiplatform project, add the artifacts to commonMain instead:
kotlin {
sourceSets {
commonMain.dependencies {
implementation(project.dependencies.platform("io.kriptal.ethers:ethers-bom:2.0.0"))
implementation("io.kriptal.ethers:ethers-abi")
implementation("io.kriptal.ethers:ethers-core")
implementation("io.kriptal.ethers:ethers-providers")
implementation("io.kriptal.ethers:ethers-signers")
}
}
}To interact with the chain, you need to create a Provider instance, which is the main entry point for all RPC calls.
// create a provider, using a websocket as underlying transport
val provider = Provider.builder("<WS_URL>").buildAwait().unwrap()
// query the latest block number
val startBlockNum = provider.getBlockNumber().sendAwait().unwrap()
println("Starting at block $startBlockNum")
// subscribe to new blocks, blocking the calling thread. Use "forEachAsync" to stream without blocking the caller.
provider.subscribeNewHeads().sendAwait().unwrap().forEach {
println("New Block: ${it.number}, ${it.number - startBlockNum} blocks since start")
}buildAwait and sendAwait block the calling thread and exist only on JVM and Android. From common code — or any
coroutine — use the suspending build() and send() instead:
val provider = Provider.builder("<WS_URL>").build().unwrap()
val startBlockNum = provider.getBlockNumber().send().unwrap()
println("Starting at block $startBlockNum")
// "forEach" consumes on the calling thread on every platform. Its non-blocking counterpart, "forEachAsync",
// is JVM/Android-only - from common code, run this on a dispatcher of your choosing.
provider.subscribeNewHeads().send().unwrap().forEach {
println("New Block: ${it.number}, ${it.number - startBlockNum} blocks since start")
}Code is structured into multiple modules, each categorized by its purpose. Below is a brief overview of each module. For a more in-depth explanation, please refer to the individual module's README.md.
abi: Provides ABI primitives with encoding/decoding logic for all types supported by the EVM.
abigen: Code for generating type-safe smart contract bindings from JSON-ABI files.
abigen-plugin: Gradle plugin for generating smart contract bindings during the build process.
core: Contains optimized base types.
crypto: Includes cryptographic utilities for signing and verifying ECDSA signatures on the secp256k1 curve.
ens: Full support for ENS names and avatars, with wildcard resolution and offchain resolution
via CCIP-Read. EnsResolver resolves explicitly with typed errors, while EnsMiddleware is a
Middleware layer that accepts an ENS name anywhere a call request is expected.
providers: Logic for interacting with JSON-RPC API using various transports (HTTP, WebSocket).
rlp: Handles the encoding and decoding of RLP.
signers: Code for transaction/message signing, allowing multiple signing key sources:
hardware wallet, mnemonic or raw private key.
signers-gcp: Signer backed by Google Cloud KMS. JVM-only.
logger: Lightweight logging facade used across the other modules.
We are happy to have you here! Opportunities to get involved with ethers-kt are open to everyone, no matter your level of expertise. Please check the CONTRIBUTING.md to get started.
Before submitting a PR make sure to format the code and run all checks using the following command:
./gradlew ktlintFormat checkcheck builds and tests the Apple targets too, and Kotlin/Native cannot cross-compile those, so the command above only
completes on a macOS host. On Linux or Windows, run the JVM and Android half and let CI cover the rest:
./gradlew jvmKotest :ethers-abigen-plugin:testFormatting is source-set scoped there as well — see the java-test job in
pull-request-checks.yml for the exact task list CI uses on Linux.
First, check if any of the README files under each module answers your question. If the answer is not there please don't open an issue. Instead, you can open a thread under Discussions.
This library has been made possible thanks to the inspiration provided by the following projects:
Proof of Work: 0x6b0f9ff6f53ec22d8d2d92b1beb193cdc523628951b5c81779fabce9f51db351