Learn how contract serialize data for function calls and storage.
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
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.
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.
When a user calls the method, the contract receives the arguments encoded as a JSON string
(e.g. "{\"a_number\":0, \"b_number\":\"100\"}"), and proceed to (de)serialize them into
the correct object (A{0, 100}) .
When returning the result, the contract will automatically encode the object B{true, 0}
into its JSON serialized value: "{\"success\":true, \"other_number\":0}" and return this
string.
JSON Limitations
Since JSON is limited to 52 bytes numbers, you cannot use u64/u128 as input
or output. JSON simply cannot serialize them. Instead, you must use Strings.The NEAR SDK RS currently implements the near_sdk::json_types::{U64, I64, U128, I128}
that you can use for input / output of data.
Go: u64 as strings
The Go SDK follows the same rule. Use string for uint64 values in function parameters and return types when they may exceed 53 bits. For 128-bit token amounts, use types.Uint128 from the Go SDK.
// Use string for large numbers passed as function argumentstype MyInput struct { Amount string `json:"amount"` // pass as "1000000000000000000000000"}
Under the hood smart contracts store data using simple key/value pairs. This means that
the contract needs to translate complex states into simple key-value pairs.For this, NEAR contracts use borsh which is optimized for (de)serializing
complex objects into smaller streams of bytes.
SDK-JS still uses json
The JavaScript SDK uses JSON to serialize objects in the state, but the borsh implementation
should arrive soon
Go SDK uses JSON for stateThe Go SDK also uses JSON (not Borsh) to serialize the contract state. Struct fields must have json:"field_name" tags for correct serialization and deserialization.
Since the Contract has a structure string, Vector<u8> the value is interpreted as:
[2, 0, 0, 0, "h", "i"] -> The `string` has 2 elements: "h" and "i".[1, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, "prefix"] -> The Vector has 1 element, and to see the values search for keys that start with (the 6 bytes prefix): "prefix"
Then, the second key-value shows the entries of the Vector denoted by the "prefix" string:
We can see that the STATE key changes to reflect the storage of the new string (bye), and that
the vector now has 2 elements.At the same time, a new key-value was added adding the new vector entry: the 1u8 we just added.
When somebody invokes a smart contract method, the first step for the contract is to deserialize
its own state.In the example used above, the contract will start by reading the STATE key and
try to deserialize its value into an object Contract{string: String, vector: Vector<u8>}.If you deploy a contract into the account with a different Contract structure, then the
contract will fail to deserialize the STATE key and panic Cannot deserialize the contract state.To solve this, you can either: