Skip to content

Commit

Permalink
expose RPC objects and add arrow extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
wiyarmir committed Sep 9, 2024
1 parent 64ef040 commit e7005fd
Show file tree
Hide file tree
Showing 25 changed files with 314 additions and 181 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=0.1.15
version=0.2.0
# Kotlin
#kotlin.code.style=official
kotlin.js.compiler=ir
Expand Down
2 changes: 2 additions & 0 deletions libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mavenPublish = "0.29.0"
skie = "0.8.2"

# Libraries
arrow = "1.2.4"
junit5 = "5.11.0"
junitPioneer = "2.2.0"
kermit = "2.0.4"
Expand All @@ -25,6 +26,7 @@ okio = "3.9.0"
serialization = "1.7.1"

[libraries]
arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" }
bouncyCastle = { module = "org.bouncycastle:bcpkix-jdk15to18", version = "1.78.1" }
coreLibraryDesugaring = { module = "com.android.tools:desugar_jdk_libs", version = "2.1.0" }
coroutinesAndroid = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencyResolutionManagement {
}

include(":solana-kotlin")
include(":solana-kotlin-arrow-extensions")
include(":tweetnacl-multiplatform")

dependencyResolutionManagement {
Expand Down
116 changes: 116 additions & 0 deletions solana-kotlin-arrow-extensions/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinSerialization)
alias(libs.plugins.mavenPublish)
alias(libs.plugins.dokka)
signing
}

group = "net.avianlabs.solana"
version = properties["version"] as String


kotlin {
applyDefaultHierarchyTemplate()
explicitApi()

jvm {
// set the target JVM version
compilations.all {
kotlinOptions {
jvmTarget = "17"
}
}
}

mingwX64()
linuxX64()

sourceSets {
val jvmMain by getting

val jvmTest by getting

val commonMain by getting {
dependencies {
api(project(":solana-kotlin"))
implementation(libs.coroutinesCore)
implementation(libs.kermit)
implementation(libs.arrow.core)
}
}
val commonTest by getting {
dependencies {
implementation(libs.kotlinTest)
implementation(libs.coroutinesTest)
}
}

val nativeMain by getting

val linuxMain by getting

val mingwMain by getting
}
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = "17"
}
}

signing {
useGpgCmd()
}

publishing {
repositories {
mavenLocal()
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/avianlabs/solana-kotlin")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
}
publications {
withType<MavenPublication> {
pom {
name = "Solana Kotlin Arrow Extensions"
description = "Arrow extensions for Solana Kotlin"
licenses {
license {
name = "MIT"
url = "https://opensource.org/licenses/MIT"
}
}
url = "https://github.com/avianlabs/solana-kotlin"
issueManagement {
system = "GitHub"
url = "https://github.com/avianlabs/solana-kotlin"
}
scm {
connection = "https://github.com/avianlabs/solana-kotlin.git"
url = "https://github.com/avianlabs/solana-kotlin"
}
developers {
developer {
name = "Avian Labs Engineers"
email = "[email protected]"
}
}
}
}
}
}

mavenPublishing {
if (rootProject.findProperty("signPublications") != "false") {
signAllPublications()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.avianlabs.solana.arrow

import arrow.core.Either
import arrow.core.left
import arrow.core.right
import net.avianlabs.solana.arrow.SolanaKotlinError.RpcError.*
import net.avianlabs.solana.client.Response
import net.avianlabs.solana.client.RpcError

public fun <T> Response<T>.toEither(): Either<SolanaKotlinError, T> =
if (error != null) {
error!!.toRpcError().left()
} else if (result != null) {
result!!.right()
} else {
SolanaKotlinError.MalformedResponse("both error and result are null").left()
}

private fun RpcError.toRpcError(): SolanaKotlinError = when (code) {
-32700 -> ParseError(message)
-32600 -> InvalidRequest(message)
-32601 -> MethodNotFound(message)
-32602 -> InvalidParams(message)
-32603 -> InternalError(message)
in -32000 downTo -32099 -> ServerError(message)
else -> SolanaKotlinError.UnknownError(message)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.avianlabs.solana.arrow

public sealed interface SolanaKotlinError {
public sealed interface RpcError : SolanaKotlinError {
public data class ParseError(val message: String) : RpcError
public data class InvalidRequest(val message: String) : RpcError
public data class MethodNotFound(val message: String) : RpcError
public data class InvalidParams(val message: String) : RpcError
public data class InternalError(val message: String) : RpcError
public data class ServerError(val message: String) : RpcError
}

public data class MalformedResponse(val message: String) : SolanaKotlinError
public data class UnknownError(val message: String) : SolanaKotlinError
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package net.avianlabs.solana

import io.ktor.client.*
import io.ktor.http.*
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import net.avianlabs.solana.client.Response
import net.avianlabs.solana.client.RpcInvocation
import net.avianlabs.solana.client.RpcKtorClient
import kotlin.coroutines.resume
Expand Down Expand Up @@ -35,7 +35,6 @@ public class SolanaClient(
),
)

@OptIn(ExperimentalSerializationApi::class)
internal val json: Json = Json {
ignoreUnknownKeys = true
isLenient = true
Expand All @@ -46,8 +45,8 @@ public class SolanaClient(
internal suspend inline fun <reified T> invoke(
method: String,
params: JsonArray? = null,
): T? {
): Response<T> {
val invocation = RpcInvocation(method, params, headerProviders)
return client.invoke<JsonArray, T>(invocation).result
return client.invoke<JsonArray, T>(invocation)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ package net.avianlabs.solana.client
import kotlinx.serialization.Serializable

@Serializable
public data class RpcResponse<T>(
public data class Response<T>(
val id: Int,
val jsonrpc: String,
val result: T? = null,
val error: RpcError? = null,
) {
@Serializable
public data class RPC<T>(
val context: Context?,
val value: T? = null,
val context: Context,
val value: T,
) {

@Serializable
public data class Context(
val slot: Long,
val slot: ULong,
val apiVersion: String?,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,20 @@ public class RpcKtorClient(
}
}

internal suspend inline fun <reified T, reified R> invoke(invocation: RpcInvocation<T>): RpcResponse<R> =
internal suspend inline fun <reified T, reified R> invoke(invocation: RpcInvocation<T>): Response<R> =
execute(makeRequest(invocation))

internal inline fun <reified T> makeRequest(invocation: RpcInvocation<T>): RpcRequest<T> =
RpcRequest(requestIdGenerator.next(), invocation)

internal suspend inline fun <reified T, reified R> execute(request: RpcRequest<T>): RpcResponse<R> {
val response = ktorClient.post(url) {
internal suspend inline fun <reified T, reified R> execute(request: RpcRequest<T>): Response<R> =
ktorClient.post(url) {
contentType(ContentType.Application.Json)
setBody(request.buildBody())
setBody(request.buildBody<T>())
request.invocation.headerProviders.forEach { (header, valueProvider) ->
header(header, valueProvider())
}
}
response.body<JsonObject>()["error"]?.let {
throw ExecuteException(Json.decodeFromJsonElement<RpcError>(it))
}
return response.body()
}
}.body()

internal inline fun <reified T> RpcRequest<T>.buildBody(): JsonObject {
val body: MutableMap<String, JsonElement> = mutableMapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,25 @@ import kotlinx.serialization.json.addJsonObject
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.put
import net.avianlabs.solana.SolanaClient
import net.avianlabs.solana.client.RpcResponse
import net.avianlabs.solana.client.Response
import net.avianlabs.solana.domain.core.Commitment
import net.avianlabs.solana.tweetnacl.ed25519.PublicKey

public suspend fun SolanaClient.getAccountInfo(
publicKey: PublicKey,
commitment: Commitment? = null,
): AccountInfo? {
val result = invoke<RpcResponse.RPC<AccountInfo>>(
method = "getAccountInfo",
params = buildJsonArray {
add(publicKey.toBase58())
addJsonObject {
put("encoding", "base64")
commitment?.let {
put("commitment", it.value)
}
): Response<Response.RPC<AccountInfo>> = invoke(
method = "getAccountInfo",
params = buildJsonArray {
add(publicKey.toBase58())
addJsonObject {
put("encoding", "base64")
commitment?.let<Commitment, Unit> {
put("commitment", it.value)
}
}
)
return result!!.value
}
}
)

/**
* Account information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import kotlinx.serialization.json.addJsonObject
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.put
import net.avianlabs.solana.SolanaClient
import net.avianlabs.solana.client.RpcResponse.RPC
import net.avianlabs.solana.client.Response
import net.avianlabs.solana.client.Response.RPC
import net.avianlabs.solana.domain.core.Commitment
import net.avianlabs.solana.tweetnacl.ed25519.PublicKey

Expand All @@ -18,17 +19,14 @@ import net.avianlabs.solana.tweetnacl.ed25519.PublicKey
public suspend fun SolanaClient.getBalance(
account: PublicKey,
commitment: Commitment? = null,
): Long {
val result = invoke<RPC<Long>>(
method = "getBalance",
params = buildJsonArray {
add(account.toBase58())
commitment?.let {
addJsonObject {
put("commitment", it.value)
}
): Response<RPC<Long>> = invoke(
method = "getBalance",
params = buildJsonArray {
add(account.toBase58())
commitment?.let {
addJsonObject {
put("commitment", it.value)
}
}
)
return result!!.value!!
}
}
)
Loading

0 comments on commit e7005fd

Please sign in to comment.