Skip to content

Commit e7005fd

Browse files
committed
expose RPC objects and add arrow extensions
1 parent 64ef040 commit e7005fd

25 files changed

+314
-181
lines changed

.idea/gradle.xml

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version=0.1.15
1+
version=0.2.0
22
# Kotlin
33
#kotlin.code.style=official
44
kotlin.js.compiler=ir

libs.versions.toml

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mavenPublish = "0.29.0"
1414
skie = "0.8.2"
1515

1616
# Libraries
17+
arrow = "1.2.4"
1718
junit5 = "5.11.0"
1819
junitPioneer = "2.2.0"
1920
kermit = "2.0.4"
@@ -25,6 +26,7 @@ okio = "3.9.0"
2526
serialization = "1.7.1"
2627

2728
[libraries]
29+
arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" }
2830
bouncyCastle = { module = "org.bouncycastle:bcpkix-jdk15to18", version = "1.78.1" }
2931
coreLibraryDesugaring = { module = "com.android.tools:desugar_jdk_libs", version = "2.1.0" }
3032
coroutinesAndroid = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }

settings.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ dependencyResolutionManagement {
2626
}
2727

2828
include(":solana-kotlin")
29+
include(":solana-kotlin-arrow-extensions")
2930
include(":tweetnacl-multiplatform")
3031

3132
dependencyResolutionManagement {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
plugins {
2+
alias(libs.plugins.kotlinMultiplatform)
3+
alias(libs.plugins.kotlinSerialization)
4+
alias(libs.plugins.mavenPublish)
5+
alias(libs.plugins.dokka)
6+
signing
7+
}
8+
9+
group = "net.avianlabs.solana"
10+
version = properties["version"] as String
11+
12+
13+
kotlin {
14+
applyDefaultHierarchyTemplate()
15+
explicitApi()
16+
17+
jvm {
18+
// set the target JVM version
19+
compilations.all {
20+
kotlinOptions {
21+
jvmTarget = "17"
22+
}
23+
}
24+
}
25+
26+
mingwX64()
27+
linuxX64()
28+
29+
sourceSets {
30+
val jvmMain by getting
31+
32+
val jvmTest by getting
33+
34+
val commonMain by getting {
35+
dependencies {
36+
api(project(":solana-kotlin"))
37+
implementation(libs.coroutinesCore)
38+
implementation(libs.kermit)
39+
implementation(libs.arrow.core)
40+
}
41+
}
42+
val commonTest by getting {
43+
dependencies {
44+
implementation(libs.kotlinTest)
45+
implementation(libs.coroutinesTest)
46+
}
47+
}
48+
49+
val nativeMain by getting
50+
51+
val linuxMain by getting
52+
53+
val mingwMain by getting
54+
}
55+
}
56+
57+
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
58+
kotlinOptions {
59+
jvmTarget = "17"
60+
}
61+
}
62+
63+
signing {
64+
useGpgCmd()
65+
}
66+
67+
publishing {
68+
repositories {
69+
mavenLocal()
70+
repositories {
71+
maven {
72+
name = "GitHubPackages"
73+
url = uri("https://maven.pkg.github.com/avianlabs/solana-kotlin")
74+
credentials {
75+
username = System.getenv("GITHUB_ACTOR")
76+
password = System.getenv("GITHUB_TOKEN")
77+
}
78+
}
79+
}
80+
}
81+
publications {
82+
withType<MavenPublication> {
83+
pom {
84+
name = "Solana Kotlin Arrow Extensions"
85+
description = "Arrow extensions for Solana Kotlin"
86+
licenses {
87+
license {
88+
name = "MIT"
89+
url = "https://opensource.org/licenses/MIT"
90+
}
91+
}
92+
url = "https://github.com/avianlabs/solana-kotlin"
93+
issueManagement {
94+
system = "GitHub"
95+
url = "https://github.com/avianlabs/solana-kotlin"
96+
}
97+
scm {
98+
connection = "https://github.com/avianlabs/solana-kotlin.git"
99+
url = "https://github.com/avianlabs/solana-kotlin"
100+
}
101+
developers {
102+
developer {
103+
name = "Avian Labs Engineers"
104+
105+
}
106+
}
107+
}
108+
}
109+
}
110+
}
111+
112+
mavenPublishing {
113+
if (rootProject.findProperty("signPublications") != "false") {
114+
signAllPublications()
115+
}
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package net.avianlabs.solana.arrow
2+
3+
import arrow.core.Either
4+
import arrow.core.left
5+
import arrow.core.right
6+
import net.avianlabs.solana.arrow.SolanaKotlinError.RpcError.*
7+
import net.avianlabs.solana.client.Response
8+
import net.avianlabs.solana.client.RpcError
9+
10+
public fun <T> Response<T>.toEither(): Either<SolanaKotlinError, T> =
11+
if (error != null) {
12+
error!!.toRpcError().left()
13+
} else if (result != null) {
14+
result!!.right()
15+
} else {
16+
SolanaKotlinError.MalformedResponse("both error and result are null").left()
17+
}
18+
19+
private fun RpcError.toRpcError(): SolanaKotlinError = when (code) {
20+
-32700 -> ParseError(message)
21+
-32600 -> InvalidRequest(message)
22+
-32601 -> MethodNotFound(message)
23+
-32602 -> InvalidParams(message)
24+
-32603 -> InternalError(message)
25+
in -32000 downTo -32099 -> ServerError(message)
26+
else -> SolanaKotlinError.UnknownError(message)
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package net.avianlabs.solana.arrow
2+
3+
public sealed interface SolanaKotlinError {
4+
public sealed interface RpcError : SolanaKotlinError {
5+
public data class ParseError(val message: String) : RpcError
6+
public data class InvalidRequest(val message: String) : RpcError
7+
public data class MethodNotFound(val message: String) : RpcError
8+
public data class InvalidParams(val message: String) : RpcError
9+
public data class InternalError(val message: String) : RpcError
10+
public data class ServerError(val message: String) : RpcError
11+
}
12+
13+
public data class MalformedResponse(val message: String) : SolanaKotlinError
14+
public data class UnknownError(val message: String) : SolanaKotlinError
15+
}

solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt

+3-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package net.avianlabs.solana
22

33
import io.ktor.client.*
44
import io.ktor.http.*
5-
import kotlinx.serialization.ExperimentalSerializationApi
65
import kotlinx.serialization.json.Json
76
import kotlinx.serialization.json.JsonArray
7+
import net.avianlabs.solana.client.Response
88
import net.avianlabs.solana.client.RpcInvocation
99
import net.avianlabs.solana.client.RpcKtorClient
1010
import kotlin.coroutines.resume
@@ -35,7 +35,6 @@ public class SolanaClient(
3535
),
3636
)
3737

38-
@OptIn(ExperimentalSerializationApi::class)
3938
internal val json: Json = Json {
4039
ignoreUnknownKeys = true
4140
isLenient = true
@@ -46,8 +45,8 @@ public class SolanaClient(
4645
internal suspend inline fun <reified T> invoke(
4746
method: String,
4847
params: JsonArray? = null,
49-
): T? {
48+
): Response<T> {
5049
val invocation = RpcInvocation(method, params, headerProviders)
51-
return client.invoke<JsonArray, T>(invocation).result
50+
return client.invoke<JsonArray, T>(invocation)
5251
}
5352
}

solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/client/ExecuteException.kt

-3
This file was deleted.

solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/client/RpcResponse.kt renamed to solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/client/Response.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ package net.avianlabs.solana.client
33
import kotlinx.serialization.Serializable
44

55
@Serializable
6-
public data class RpcResponse<T>(
6+
public data class Response<T>(
77
val id: Int,
88
val jsonrpc: String,
99
val result: T? = null,
1010
val error: RpcError? = null,
1111
) {
1212
@Serializable
1313
public data class RPC<T>(
14-
val context: Context?,
15-
val value: T? = null,
14+
val context: Context,
15+
val value: T,
1616
) {
1717

1818
@Serializable
1919
public data class Context(
20-
val slot: Long,
20+
val slot: ULong,
2121
val apiVersion: String?,
2222
)
2323
}

solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/client/RpcKtorClient.kt

+5-10
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,20 @@ public class RpcKtorClient(
4040
}
4141
}
4242

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

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

49-
internal suspend inline fun <reified T, reified R> execute(request: RpcRequest<T>): RpcResponse<R> {
50-
val response = ktorClient.post(url) {
49+
internal suspend inline fun <reified T, reified R> execute(request: RpcRequest<T>): Response<R> =
50+
ktorClient.post(url) {
5151
contentType(ContentType.Application.Json)
52-
setBody(request.buildBody())
52+
setBody(request.buildBody<T>())
5353
request.invocation.headerProviders.forEach { (header, valueProvider) ->
5454
header(header, valueProvider())
5555
}
56-
}
57-
response.body<JsonObject>()["error"]?.let {
58-
throw ExecuteException(Json.decodeFromJsonElement<RpcError>(it))
59-
}
60-
return response.body()
61-
}
56+
}.body()
6257

6358
internal inline fun <reified T> RpcRequest<T>.buildBody(): JsonObject {
6459
val body: MutableMap<String, JsonElement> = mutableMapOf(

solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getAccountInfo.kt

+11-14
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,25 @@ import kotlinx.serialization.json.addJsonObject
77
import kotlinx.serialization.json.buildJsonArray
88
import kotlinx.serialization.json.put
99
import net.avianlabs.solana.SolanaClient
10-
import net.avianlabs.solana.client.RpcResponse
10+
import net.avianlabs.solana.client.Response
1111
import net.avianlabs.solana.domain.core.Commitment
1212
import net.avianlabs.solana.tweetnacl.ed25519.PublicKey
1313

1414
public suspend fun SolanaClient.getAccountInfo(
1515
publicKey: PublicKey,
1616
commitment: Commitment? = null,
17-
): AccountInfo? {
18-
val result = invoke<RpcResponse.RPC<AccountInfo>>(
19-
method = "getAccountInfo",
20-
params = buildJsonArray {
21-
add(publicKey.toBase58())
22-
addJsonObject {
23-
put("encoding", "base64")
24-
commitment?.let {
25-
put("commitment", it.value)
26-
}
17+
): Response<Response.RPC<AccountInfo>> = invoke(
18+
method = "getAccountInfo",
19+
params = buildJsonArray {
20+
add(publicKey.toBase58())
21+
addJsonObject {
22+
put("encoding", "base64")
23+
commitment?.let<Commitment, Unit> {
24+
put("commitment", it.value)
2725
}
2826
}
29-
)
30-
return result!!.value
31-
}
27+
}
28+
)
3229

3330
/**
3431
* Account information

solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getBalance.kt

+11-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import kotlinx.serialization.json.addJsonObject
55
import kotlinx.serialization.json.buildJsonArray
66
import kotlinx.serialization.json.put
77
import net.avianlabs.solana.SolanaClient
8-
import net.avianlabs.solana.client.RpcResponse.RPC
8+
import net.avianlabs.solana.client.Response
9+
import net.avianlabs.solana.client.Response.RPC
910
import net.avianlabs.solana.domain.core.Commitment
1011
import net.avianlabs.solana.tweetnacl.ed25519.PublicKey
1112

@@ -18,17 +19,14 @@ import net.avianlabs.solana.tweetnacl.ed25519.PublicKey
1819
public suspend fun SolanaClient.getBalance(
1920
account: PublicKey,
2021
commitment: Commitment? = null,
21-
): Long {
22-
val result = invoke<RPC<Long>>(
23-
method = "getBalance",
24-
params = buildJsonArray {
25-
add(account.toBase58())
26-
commitment?.let {
27-
addJsonObject {
28-
put("commitment", it.value)
29-
}
22+
): Response<RPC<Long>> = invoke(
23+
method = "getBalance",
24+
params = buildJsonArray {
25+
add(account.toBase58())
26+
commitment?.let {
27+
addJsonObject {
28+
put("commitment", it.value)
3029
}
3130
}
32-
)
33-
return result!!.value!!
34-
}
31+
}
32+
)

0 commit comments

Comments
 (0)