Notes on Serialization
Smart contracts need to be able to communicate complex data in a simple way, while also reading and storing such data into their states efficiently.
To achieve such simple communication and efficient storage, smart contracts morph the data from their complex representation into simpler ones.
This process of translating complex objects into simpler single-value representations is called serialization. NEAR uses two serialization formats: JSON and Borsh.
- JSON is used to serialize the contract's input/output during a function call
- Borsh is used to serialize the contract's state.
Overview of Serialization Formats
Let's give a quick overview of both serialization formats, including their pros and cons, as well as an example of what their serializations look like.
JSON: Objects to Strings
Features
- Self-describing format
- Easy interoperability with JavaScript
- Multiple implementations readily available
- But... it is not efficient both in computational times and resulting size
Example
Example{
number: i32 = 2;
arr: Vector<i32> = [0, 1];
}
// serializes to
"{\"number\": 2, \"arr\": [0, 1]}"
Borsh: Objects to Bytes
Features
- Compact, binary format built to be efficiently (de)serialized
- Strict and canonical binary representation
- Less overhead: it does not need to store attributes names
- But... it is necessary to know the schema to (de)serialize the data
Example
Example{
number: i32 = 2;
arr: Vector<i32> = [0, 1];
}
// serializes into
[2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
Serializing Input & Output
NEAR contracts can implement methods that both take and return complex objects. In order to handle this data in a simple way, JSON serialization is used.
Using JSON makes it easier for everyone to talk with the contracts, since most languages readily implement a JSON (de)serializer.
Example
Let's look at this example, written only for educational purposes:
#[derive(Serialize)]
#[serde(crate = "near_sdk::serde")]
pub struct A {
pub a_number: i32,
pub b_number: u128
}
#[derive(Serialize)]
#[serde(crate = "near_sdk::serde")]
pub struct B {
pub success: bool,
pub other_number: i32
}
pub fn method(&self, struct_a: A): B {
return B{true, 0}
}