Smart contract schemas

A smart contract schema is a description of how to represent bytes in a more structured representation. It can be used by external tools when displaying the state of a smart contract instance and for specifying parameters using a structured representation, such as JSON.

See also

For instructions on how to build the schema for a smart contract module in Rust, see Build a contract schema.

Why use a contract schema

Data on the blockchain, such as the state of an instance and parameters passed to init and receive functions, is serialized as a sequence of bytes. The serialization is optimized for efficiency, rather than human readability.

Usually these bytes have structure and this structure is known to the smart contract as part of the contract functions, but outside of these functions it can be difficult to make sense of the bytes. This is especially the case when inspecting a complex state of a contract instance or when passing complex parameters to a smart contract function. In the latter case, the bytes should either be serialized from structured data or written manually.

The solution for avoiding manual parsing of bytes is to capture this information in a smart contract schema, which describes how to make structure from the bytes, and can be used by external tools.

Note

The concordium-client tool can use a schema to serialize JSON parameters and to deserialize the state of contract instances to JSON.

The schema is then either embedded into a smart contract module that is deployed to the chain, or is written to a file and passed around off-chain.

Should you embed or write to a file?

Whether a contract schema should be embedded or written to a file depends on your situation.

Embedding the schema into the smart contract module distributes the schema together with the contract ensuring the correct schema is being used and also allows anyone to use it directly. The downside is that the smart contract module becomes bigger in size and therefore more expensive to deploy. But unless the smart contract uses very complex types for the state and parameters, the size of the schema is likely to be negligible compared to the size of the smart contract itself.

Having the schema in a separate file allows you to have the schema without paying for the extra bytes when deploying. The downside is that you instead have to distribute the schema file through some other channel and ensure that contract users are using the correct file with your smart contract.

The schema format

A schema can contain

  • structure information for a smart contract module

  • description of smart-contract state

  • parameters for init and receive functions of a smart contract.

Each of these descriptions is referred to as a schema type. Schema types are always optional to include in a schema.

Currently the supported schema types are inspired by what is commonly used in the Rust programming language:

enum Type {
    Unit,
    Bool,
    U8,
    U16,
    U32,
    U64,
    I8,
    I16,
    I32,
    I64,
    Amount,
    AccountAddress,
    ContractAddress,
    Timestamp,
    Duration,
    Pair(Type, Type),
    List(SizeLength, Type),
    Set(SizeLength, Type),
    Map(SizeLength, Type, Type),
    Array(u32, Type),
    Struct(Fields),
    Enum(List (String, Fields)),
}

enum Fields {
    Named(List (String, Type)),
    Unnamed(List Type),
    Empty,
}

Here, SizeLength describes the number of bytes used to describe the length of a variable length type, such as List.

enum SizeLength {
    One,
    Two,
    Four,
    Eight,
}

For a reference on how a schema type is serialized into bytes, we refer the reader to the implementation in Rust.

Embedding schemas on-chain

Schemas are embedded into smart contract modules using the custom section feature of Wasm modules. This allows Wasm modules to include a named section of bytes, which does not affect the semantics of running the Wasm module.

All schemas are collected and added in one custom section named concordium-schema-v1. This collection is a list of pairs, containing the name of the contract encoded in UTF-8 and the contract schema bytes.