Using Adapter Provider

Speaking of ScaleCodecAdapterProvider, it's not very simple.

It's defined as an abstract class, thus have all of the methods already predefined for your convenience. But you would need to configure it manually from scratch. Please don't hesitate to look at this class declaration or directly from source code to inherit from this.

But we have our default provider implementation called DefaultScaleCodecAdapterProvider.

It has all of our adapters implementations in it already, so you can empower it by adding special behavior, same as if you would create your own provider.

To provider an adapter for some static type without any generics, it's simple as that:

val provider: ScaleCodecAdapterProvider
provider.setAdapter(MyCustomTypeAdapter(), MyCustomType::class)

But if this is a some generic type, you might want to go with factory for this adapter:

val provider: ScaleCodecAdapterProvider
provider.setAdapter(object : ScaleCodecAdapterFactory {
    override fun <T> make() = MyCustomTypeAdapter<T>() as ScaleCodecAdapter<T>
}, MyCustomType::class)

Even though we have an OptionalAdapter, some types, like Boolean in Substrate, has different serialization behavior. In order to handle custom optionals before going with default optional adapter, feel free to add your custom adapter:

val provider: ScaleCodecAdapterProvider
provider.setNullableAdapter(object : ScaleCodecAdapterFactory {
    override fun <T> make() = MyCustomTypeNullableAdapter<T>() as ScaleCodecAdapter<T>
}, MyCustomType::class)

If there is some custom type, that might be instance of any class in fact. Like any structs in Substrate, which might be data or regular class in Kotlin, provide the factory for this like that:

val provider: ScaleCodecAdapterProvider
provider.addGenericAdapter(object : ScaleCodecAdapterFactory {
    override fun <T> make() = MyCustomTypeAdapter<T>() as ScaleCodecAdapter<T>

And don't forget to handle this inside your adapter, because this adapter will be applied to any potential class. This might be some interface which class conforms to, or some annotation in it. If this class doesn't conform to your conditions, feel free to throw an error! This is how our adapter provider finds out that this adapter isn't suitable for given object.

And like we used in our enums, this factory might be tried "conditionally":

val factory = object: ScaleCodecAdapterFactory {
    override fun <T> make() = MyCustomTypeAdapter<Any>() as ScaleCodecAdapter<T>

addConditionalAdapter(factory = factory) { type ->
    type.findAnnotation<MyCustomTypeAnnotation>() != null


This will faster determine your adapter for a given type if it has this annotation. You might add additional conditions, like if there's an interface in this class declaration and so on.

Once the type is resolved using any adapter. This match is being cached for whole codec lifecycle to resolve adapter faster. So please take this into consideration.

Last updated