Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package xyz.mcxross.kaptos.model
import kotlinx.serialization.Serializable
import xyz.mcxross.bcs.Bcs
import xyz.mcxross.kaptos.core.Hex
import xyz.mcxross.kaptos.serialize.MoveOptionSerializer
import xyz.mcxross.kaptos.serialize.MoveStringSerializer
import xyz.mcxross.kaptos.serialize.MoveVectorSerializer

Expand Down Expand Up @@ -141,7 +142,7 @@ data class MoveString(val value: String) : TransactionArgument() {
}
}

@Serializable
@Serializable(with = MoveOptionSerializer::class)
data class MoveOption<T : EntryFunctionArgument>(val value: T?) : TransactionArgument() {
fun unwrap(): T {
return value ?: throw IllegalArgumentException("Option is empty")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2024 McXross
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.mcxross.kaptos.serialize

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.listSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.encoding.encodeCollection
import xyz.mcxross.kaptos.model.EntryFunctionArgument
import xyz.mcxross.kaptos.model.MoveOption

class MoveOptionSerializer<T : EntryFunctionArgument>(
private val elementSerializer: KSerializer<T>
) : KSerializer<MoveOption<T>> {
@OptIn(ExperimentalSerializationApi::class)
override val descriptor: SerialDescriptor = listSerialDescriptor(elementSerializer.descriptor)

override fun serialize(encoder: Encoder, value: MoveOption<T>) {
value.value?.let {
encoder.encodeCollection(descriptor, 1) { elementSerializer.serialize(encoder, it) }
} ?: run {
encoder.encodeByte(0)
}
}

override fun deserialize(decoder: Decoder): MoveOption<T> {
return MoveOption(
decoder.decodeStructure(descriptor) {
val isSome = decodeCollectionSize(descriptor)
require(isSome < 2) { "Expected length of 0 or 1 for MoveOption, found $isSome" }

if (isSome == 1) {
decodeSerializableElement(descriptor, 0, elementSerializer)
} else {
null
}
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package xyz.mcxross.kaptos.unit.serialize

import xyz.mcxross.bcs.Bcs
import kotlin.test.Test
import xyz.mcxross.kaptos.model.MoveOption
import xyz.mcxross.kaptos.model.U8
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull

class MoveOptionSerializerTest {
@Test
fun `can deserialize a Some option (vector of length 1)`() {
val encoded = listOf(1.toByte(), 42.toByte()).toByteArray()

val decoded: MoveOption<U8> = Bcs.decodeFromByteArray(encoded)
val expected = MoveOption(U8(42.toByte()))

assertEquals(expected.value?.value, decoded.value?.value)
}

@Test
fun `can deserialize a None option (vector of length 0)`() {
val encoded = listOf(0.toByte()).toByteArray()

val decoded: MoveOption<U8> = Bcs.decodeFromByteArray(encoded)
val expected = MoveOption<U8>(null)

assertNull(decoded.value)
assertEquals(expected.value, decoded.value)
}

@Test
fun `unwrap returns value when Some`() {
val encoded = listOf(1.toByte(), 100.toByte()).toByteArray()

val decoded: MoveOption<U8> = Bcs.decodeFromByteArray(encoded)

assertEquals(100.toByte(), decoded.unwrap().value)
}

@Test
fun `unwrap throws when None`() {
val encoded = listOf(0.toByte()).toByteArray()

val decoded: MoveOption<U8> = Bcs.decodeFromByteArray(encoded)

assertFailsWith<IllegalArgumentException> {
decoded.unwrap()
}
}

@Test
fun `should throw error when deserializing empty byte array`() {
val emptyInput = byteArrayOf()

assertFailsWith<Exception> {
Bcs.decodeFromByteArray<MoveOption<U8>>(emptyInput)
}
}

@Test
fun `should throw error when deserializing vector with length greater than 1`() {
val encoded = listOf(2.toByte(), 1.toByte(), 2.toByte()).toByteArray()

assertFailsWith<IllegalArgumentException> {
Bcs.decodeFromByteArray<MoveOption<U8>>(encoded)
}
}
}
Loading