diff --git a/solana/src/commonMain/kotlin/com/solana/publickey/SolanaPublicKey.kt b/solana/src/commonMain/kotlin/com/solana/publickey/SolanaPublicKey.kt index ed8e830..0803a71 100644 --- a/solana/src/commonMain/kotlin/com/solana/publickey/SolanaPublicKey.kt +++ b/solana/src/commonMain/kotlin/com/solana/publickey/SolanaPublicKey.kt @@ -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 { @@ -35,13 +42,35 @@ open class SolanaPublicKey(final override val bytes: ByteArray) : PublicKey { } object SolanaPublicKeySerializer : KSerializer { - 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) + } + } } diff --git a/solana/src/commonTest/kotlin/com/solana/serialization/SolanaPublicKeySerializerTests.kt b/solana/src/commonTest/kotlin/com/solana/serialization/SolanaPublicKeySerializerTests.kt new file mode 100644 index 0000000..414a69a --- /dev/null +++ b/solana/src/commonTest/kotlin/com/solana/serialization/SolanaPublicKeySerializerTests.kt @@ -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(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) + } +} \ No newline at end of file