Skip to content

Instantly share code, notes, and snippets.

@mfornet
Last active November 18, 2021 04:30
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mfornet/d8a94af333a68d67affd8cb78464c7c0 to your computer and use it in GitHub Desktop.
Save mfornet/d8a94af333a68d67affd8cb78464c7c0 to your computer and use it in GitHub Desktop.
Borsh I/O in NEAR Smart Contracts

Borsh I/O in NEAR Smart Contracts

Manual encoding single parameter

pub fn set_status_borsh(&mut self, #[serializer(borsh)] message: Vec<u8>) {}

To call this function you need to pass arguments borsh serialized, and those bytes encoded as base64

Suppose the arguments are as follows:

message = [72, 101, 108, 108, 111, 32, 77, 105, 107, 101, 33]

You encode it as follows following borsh specification (https://borsh.io/)

import base64
import struct

def borsh_encode_list_u8(data):
    """
    The first four bytes is the length of the data, and then the data concatenated
    """
    length = len(data)
    return struct.pack('I', length) + bytes(data)


borshified_message = borsh_encode_list_u8(message)
print(base64.b64encode(borshified_message))

The output of the previous script is the message borshfied and encoded with base64:

b'CwAAAEhlbGxvIE1pa2Uh'

To call the function from NEAR-CLI use --base64 flag to indicate the input is not json:

near call status.near set_status_borsh --base64 'CwAAAEhlbGxvIE1pa2Uh' --accountId mike.near

Multiple input parameters

If you need to pass several arguments at once to a function you need to borsh serialize all the arguments, and concatenate them.

pub fn set_metadata_borsh(&mut self, 
                          #[serializer(borsh)] name: String, 
                          #[serializer(borsh)] symbol: String, 
                          #[serializer(borsh)] decimals: u8) {}

This is the same as creating a new struct with all this arguments as fields, and borsh serialize it:

#[derive(BorshSerialize)]
struct SetMetadataInput {
    name: String,
    symbol: String,
    decimals: u8,
}

Use the following snippet to generate the input:

#[test]
fn generate_input() {
    let input = SetMetadataInput {
        name: "ChainLink Token".to_string(),
        symbol: "LINK".to_string(),
        decimals: 18,
    };

    let serialized: Vec<u8> = input.try_to_vec().unwrap();
    println!("{:?}", to_base64(serialized));
}

The output of the previous rust code is:

"DwAAAENoYWluTGluayBUb2tlbgQAAABMSU5LEg=="

Call function set_metadata_borsh from NEAR-CLI using:

near call status.near set_metadata_borsh --base64 'DwAAAENoYWluTGluayBUb2tlbgQAAABMSU5LEg==' --accountId mike.near

Output parameters

It is possible the output of some function is also borsh serialized. The will usually looks like:

#[result_serializer(borsh)]
fn get_name(&self) -> String {
    "Hello".to_string()
}

It is not possible to parse this arguments from NEAR-CLI currently, mostly because NEAR-CLI doesn't know the schema. But you can do it from near-api-js.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment