Skip to content

Commit

Permalink
Add JSON serialization support for SolanaPublicKey (#30)
Browse files Browse the repository at this point in the history
* add json serialization support for SolanaPublicKey

* fix transaction encoding
  • Loading branch information
Funkatronics authored Jun 13, 2024
1 parent f0f33e8 commit e5a9205
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package com.solana.publickey

import com.funkatronics.encoders.Base58
import com.funkatronics.kborsh.BorshDecoder
import com.funkatronics.kborsh.BorshEncoder
import com.solana.serialization.ByteStringSerializer
import com.solana.serialization.TransactionDecoder
import com.solana.serialization.TransactionEncoder
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder

@Serializable(with=SolanaPublicKeySerializer::class)
open class SolanaPublicKey(final override val bytes: ByteArray) : PublicKey {
Expand Down Expand Up @@ -35,13 +42,35 @@ open class SolanaPublicKey(final override val bytes: ByteArray) : PublicKey {
}

object SolanaPublicKeySerializer : KSerializer<SolanaPublicKey> {
private val delegate = ByteStringSerializer(SolanaPublicKey.PUBLIC_KEY_LENGTH)
override val descriptor: SerialDescriptor = delegate.descriptor
private val borshDelegate = ByteStringSerializer(SolanaPublicKey.PUBLIC_KEY_LENGTH)
private val jsonDelegate = String.serializer()
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("SolanaPublicKey")

override fun deserialize(decoder: Decoder): SolanaPublicKey =
SolanaPublicKey(decoder.decodeSerializableValue(delegate))
when (decoder) {
is BorshDecoder, is TransactionDecoder ->
SolanaPublicKey(decoder.decodeSerializableValue(borshDelegate))
is JsonDecoder ->
SolanaPublicKey.from(decoder.decodeSerializableValue(jsonDelegate))
else ->
runCatching {
SolanaPublicKey.from(decoder.decodeSerializableValue(jsonDelegate))
}.getOrElse {
SolanaPublicKey(decoder.decodeSerializableValue(borshDelegate))
}
}

override fun serialize(encoder: Encoder, value: SolanaPublicKey) {
encoder.encodeSerializableValue(delegate, value.bytes)
}
override fun serialize(encoder: Encoder, value: SolanaPublicKey) =
when (encoder) {
is BorshEncoder, is TransactionEncoder ->
encoder.encodeSerializableValue(borshDelegate, value.bytes)
is JsonDecoder ->
encoder.encodeSerializableValue(jsonDelegate, value.base58())
else ->
runCatching {
encoder.encodeSerializableValue(jsonDelegate, value.base58())
}.getOrElse {
encoder.encodeSerializableValue(borshDelegate, value.bytes)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.solana.serialization

import com.funkatronics.kborsh.Borsh
import com.solana.publickey.SolanaPublicKey
import com.solana.publickey.SolanaPublicKeySerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals

class SolanaPublicKeySerializerTests {

@Test
fun `Json serializes as base58 string`() {
// given
val publicKeyBase58 = "11111111111111111111111111111111"
val publicKey = SolanaPublicKey.from(publicKeyBase58)

// when
val serialized = Json.encodeToString(SolanaPublicKeySerializer, publicKey)

// then
assertEquals("\"$publicKeyBase58\"", serialized)
}

@Test
fun `Json deserializes from base58 string`() {
// given
@Serializable
data class TestStruct(val owner: SolanaPublicKey)

val publicKeyBase58 = "11111111111111111111111111111111"
val publicKey = SolanaPublicKey.from(publicKeyBase58)
val json = """
{
"owner": "$publicKeyBase58"
}
""".trimIndent()

// when
val deserialized = Json.decodeFromString<TestStruct>(json)

// then
assertEquals(publicKey, deserialized.owner)
}

@Test
fun `Borsh serializes as base58 string`() {
// given
val publicKeyBase58 = "11111111111111111111111111111111"
val publicKey = SolanaPublicKey.from(publicKeyBase58)

// when
val serialized = Borsh.encodeToByteArray(SolanaPublicKeySerializer, publicKey)

// then
assertContentEquals(publicKey.bytes, serialized)
}

@Test
fun `Borsh deserializes from base58 string`() {
// given
val publicKeyBase58 = "11111111111111111111111111111111"
val publicKey = SolanaPublicKey.from(publicKeyBase58)

// when
val deserialized = Borsh.decodeFromByteArray(SolanaPublicKeySerializer, publicKey.bytes)

// then
assertEquals(publicKey, deserialized)
}

@Test
fun `Transaction encoder encodes public key bytes`() {
// given
val publicKeyBase58 = "11111111111111111111111111111111"
val publicKey = SolanaPublicKey.from(publicKeyBase58)

// when
val serialized = TransactionFormat.encodeToByteArray(SolanaPublicKeySerializer, publicKey)

// then
assertContentEquals(publicKey.bytes, serialized)
}

@Test
fun `Transaction encoder decodes public key bytes`() {
// given
val publicKeyBase58 = "11111111111111111111111111111111"
val publicKey = SolanaPublicKey.from(publicKeyBase58)

// when
val deserialized = TransactionFormat.decodeFromByteArray(SolanaPublicKeySerializer, publicKey.bytes)

// then
assertEquals(publicKey, deserialized)
}
}

0 comments on commit e5a9205

Please sign in to comment.