diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt index cd9ab5a..9b82564 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/SolanaClient.kt @@ -1,5 +1,6 @@ package net.avianlabs.solana +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray import net.avianlabs.solana.client.RpcInvocation @@ -9,10 +10,12 @@ public class SolanaClient( private val client: RpcKtorClient, ) { + @OptIn(ExperimentalSerializationApi::class) internal val json: Json = Json { ignoreUnknownKeys = true isLenient = true allowSpecialFloatingPointValues = true + explicitNulls = false } internal suspend inline fun invoke( diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/domain/core/Commitment.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/domain/core/Commitment.kt new file mode 100644 index 0000000..464d940 --- /dev/null +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/domain/core/Commitment.kt @@ -0,0 +1,18 @@ +package net.avianlabs.solana.domain.core + + +/** + * [Commitment.Finalized] - the node will query the most recent block confirmed by supermajority of the cluster as having reached maximum lockout, meaning the cluster has recognized this block as finalized + * [Commitment.Confirmed] - the node will query the most recent block that has been voted on by supermajority of the cluster. + * It incorporates votes from gossip and replay. + * It does not count votes on descendants of a block, only direct votes on that block. + * This confirmation level also upholds "optimistic confirmation" guarantees in release 1.3 and onwards. + * [Commitment.Processed] - the node will query its most recent block. Note that the block may still be skipped by the cluster. + * + * @see Configuring state commitment + */ +public enum class Commitment(public val value: String) { + Finalized("finalized"), + Confirmed("confirmed"), + Processed("processed"), +} diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getBalance.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getBalance.kt index e1fbd8a..c3f2a14 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getBalance.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getBalance.kt @@ -1,18 +1,30 @@ package net.avianlabs.solana.methods +import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient import net.avianlabs.solana.client.RpcResponse.RPC +import net.avianlabs.solana.domain.core.Commitment import net.avianlabs.solana.domain.core.PublicKey public suspend fun SolanaClient.getBalance( account: PublicKey, + commitment: Commitment? = null, ): Long { - val result = invoke>("getBalance", params(account)) + val result = invoke>("getBalance", params(account, commitment)) return result!!.value!! } private fun SolanaClient.params( account: PublicKey, -) = JsonArray(listOf(json.encodeToJsonElement(account))) + commitment: Commitment? +) = JsonArray(buildList { + add(json.encodeToJsonElement(account)) + commitment?.let { add(json.encodeToJsonElement(BalanceParams(it.value))) } +}) + +@Serializable +internal data class BalanceParams( + val commitment: String? = null, +) diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getFeeForMessage.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getFeeForMessage.kt index 3b1b78d..2fcefcd 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getFeeForMessage.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getFeeForMessage.kt @@ -1,16 +1,30 @@ package net.avianlabs.solana.methods -import io.ktor.util.* +import io.ktor.util.encodeBase64 +import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient import net.avianlabs.solana.client.RpcResponse.RPC +import net.avianlabs.solana.domain.core.Commitment -public suspend fun SolanaClient.getFeeForMessage(message: ByteArray): Long { - val result = invoke>("getFeeForMessage", params(message)) +public suspend fun SolanaClient.getFeeForMessage( + message: ByteArray, + commitment: Commitment? = null +): Long { + val result = invoke>("getFeeForMessage", params(message, commitment)) return result!!.value!! } private fun SolanaClient.params( message: ByteArray, -) = JsonArray(listOf(json.encodeToJsonElement(message.encodeBase64()))) + commitment: Commitment?, +) = JsonArray(buildList { + add(json.encodeToJsonElement(message.encodeBase64())) + commitment?.let { add(json.encodeToJsonElement(FeeForMessageParams(it.value))) } +}) + +@Serializable +internal data class FeeForMessageParams( + val commitment: String? = null, +) diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getMinimumBalanceForRentExemption.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getMinimumBalanceForRentExemption.kt index 46722f7..18a6513 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getMinimumBalanceForRentExemption.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getMinimumBalanceForRentExemption.kt @@ -4,10 +4,11 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient +import net.avianlabs.solana.domain.core.Commitment public suspend fun SolanaClient.getMinimumBalanceForRentExemption( dataLength: Long, - commitment: String = "confirmed", + commitment: Commitment? = null, ): Long { val result = invoke("getMinimumBalanceForRentExemption", params(dataLength, commitment)) return result!! @@ -15,15 +16,13 @@ public suspend fun SolanaClient.getMinimumBalanceForRentExemption( private fun SolanaClient.params( dataLength: Long, - commitment: String, -) = JsonArray( - listOf( - json.encodeToJsonElement(dataLength), - json.encodeToJsonElement(GetMinimumBalanceForRentExemptionParams(commitment)) - ) -) + commitment: Commitment? +) = JsonArray(buildList { + add(json.encodeToJsonElement(dataLength)) + commitment?.let { add(json.encodeToJsonElement(GetMinimumBalanceForRentExemptionParams(it.value))) } +}) @Serializable internal data class GetMinimumBalanceForRentExemptionParams( - val commitment: String, + val commitment: String? = null, ) diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getRecentBlockhash.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getRecentBlockhash.kt index 781b37b..5ee7857 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getRecentBlockhash.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getRecentBlockhash.kt @@ -5,17 +5,20 @@ import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient import net.avianlabs.solana.client.RpcResponse +import net.avianlabs.solana.domain.core.Commitment public suspend fun SolanaClient.getRecentBlockhash( - commitment: String? = null, + commitment: Commitment? = null, ): RecentBlockHash { val rpc = invoke>("getRecentBlockhash", params(commitment)) return rpc!!.value!! } private fun SolanaClient.params( - commitment: String? = null, -) = JsonArray(listOf(json.encodeToJsonElement(commitment))) + commitment: Commitment?, +) = JsonArray(buildList { + commitment?.let { add(json.encodeToJsonElement(it.value)) } +}) @Serializable public data class RecentBlockHash( diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getSignaturesForAddress.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getSignaturesForAddress.kt index 7e8fb8b..42229c4 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getSignaturesForAddress.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getSignaturesForAddress.kt @@ -5,27 +5,30 @@ import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient +import net.avianlabs.solana.domain.core.Commitment import net.avianlabs.solana.domain.core.PublicKey public suspend fun SolanaClient.getSignaturesForAddress( account: PublicKey, - commitment: String?, + commitment: Commitment? = null, ): List { - val rpcParams = SignaturesForAddress( - commitment = commitment, + val result = invoke>( + "getSignaturesForAddress", + params(account, commitment) ) - val result = - invoke>("getSignaturesForAddress", params(account, rpcParams)) return result!! } private fun SolanaClient.params( account: PublicKey, - rpcParams: SignaturesForAddress, -) = JsonArray(listOf(json.encodeToJsonElement(account), json.encodeToJsonElement(rpcParams))) + commitment: Commitment? +) = JsonArray(buildList { + add(json.encodeToJsonElement(account)) + commitment?.let { add(json.encodeToJsonElement(SignaturesForAddressParams(commitment = it.value))) } +}) @Serializable -internal data class SignaturesForAddress( +internal data class SignaturesForAddressParams( val limit: Long? = null, val before: String? = null, val until: String? = null, diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getTokenAccountBalance.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getTokenAccountBalance.kt index 5b3ec4a..2eec5b2 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getTokenAccountBalance.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getTokenAccountBalance.kt @@ -5,18 +5,29 @@ import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient import net.avianlabs.solana.client.RpcResponse.RPC +import net.avianlabs.solana.domain.core.Commitment import net.avianlabs.solana.domain.core.PublicKey public suspend fun SolanaClient.getTokenAccountBalance( tokenAccount: PublicKey, + commitment: Commitment? = null, ): TokenAmountInfo { - val result = invoke>("getTokenAccountBalance", params(tokenAccount)) + val result = invoke>("getTokenAccountBalance", params(tokenAccount, commitment)) return result!!.value!! } private fun SolanaClient.params( account: PublicKey, -) = JsonArray(listOf(json.encodeToJsonElement(account))) + commitment: Commitment? +) = JsonArray(buildList { + add(json.encodeToJsonElement(account)) + commitment?.let { json.encodeToJsonElement(TokenAccountBalanceParams(it.value)) } +}) + +@Serializable +internal data class TokenAccountBalanceParams( + val commitment: String? = null +) @Serializable public data class TokenAmountInfo( diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getTransaction.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getTransaction.kt index 1979e20..c571267 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getTransaction.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/getTransaction.kt @@ -5,21 +5,22 @@ import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient +import net.avianlabs.solana.domain.core.Commitment public suspend fun SolanaClient.getTransaction( signature: String, - commitment: String? = null, + commitment: Commitment? = null, ): TransactionResponse? { - val rpcParams = GetTransactionParams( - commitment = commitment, - ) - return invoke("getTransaction", params(signature, rpcParams)) + return invoke("getTransaction", params(signature, commitment)) } private fun SolanaClient.params( signature: String, - rpcParams: GetTransactionParams, -) = JsonArray(listOf(json.encodeToJsonElement(signature), json.encodeToJsonElement(rpcParams))) + commitment: Commitment? +) = JsonArray(buildList { + add(json.encodeToJsonElement(signature)) + commitment?.let { add(json.encodeToJsonElement(GetTransactionParams(it.value))) } +}) @Serializable internal data class GetTransactionParams( diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/isBlockHashValid.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/isBlockHashValid.kt index cd22196..3a098cc 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/isBlockHashValid.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/isBlockHashValid.kt @@ -5,10 +5,11 @@ import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient import net.avianlabs.solana.client.RpcResponse.RPC +import net.avianlabs.solana.domain.core.Commitment public suspend fun SolanaClient.isBlockHashValid( blockHash: String, - commitment: String? = null, + commitment: Commitment? = null, minContextSlot: Long? = null, ): Boolean { val result = @@ -18,14 +19,14 @@ public suspend fun SolanaClient.isBlockHashValid( private fun SolanaClient.params( blockHash: String, - commitment: String? = null, - minContextSlot: Long? = null, -) = JsonArray( - listOf( - json.encodeToJsonElement(blockHash), - json.encodeToJsonElement(IsBlockHashValidParams(commitment, minContextSlot)) - ) -) + commitment: Commitment?, + minContextSlot: Long?, +) = JsonArray(buildList { + add(json.encodeToJsonElement(blockHash)) + if (commitment != null || minContextSlot != null) { + add(json.encodeToJsonElement(IsBlockHashValidParams(commitment?.value, minContextSlot))) + } +}) @Serializable internal data class IsBlockHashValidParams( diff --git a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/requestAirdrop.kt b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/requestAirdrop.kt index 4b599a4..6f393c3 100644 --- a/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/requestAirdrop.kt +++ b/solana-kotlin/src/commonMain/kotlin/net/avianlabs/solana/methods/requestAirdrop.kt @@ -1,19 +1,32 @@ package net.avianlabs.solana.methods +import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.encodeToJsonElement import net.avianlabs.solana.SolanaClient +import net.avianlabs.solana.domain.core.Commitment import net.avianlabs.solana.domain.core.PublicKey public suspend fun SolanaClient.requestAirdrop( publicKey: PublicKey, lamports: Long, + commitment: Commitment? = null ): String { - val result = invoke("requestAirdrop", params(publicKey, lamports)) + val result = invoke("requestAirdrop", params(publicKey, lamports, commitment)) return result!! } private fun SolanaClient.params( publicKey: PublicKey, lamports: Long, -) = JsonArray(listOf(json.encodeToJsonElement(publicKey), json.encodeToJsonElement(lamports))) + commitment: Commitment? +) = JsonArray(buildList { + add(json.encodeToJsonElement(publicKey)) + add(json.encodeToJsonElement(lamports)) + commitment?.let { json.encodeToJsonElement(RequestAirdropParams(it.value)) } +}) + +@Serializable +internal data class RequestAirdropParams( + val commitment: String? = null, +)