near-bindgen

Rust library for writing NEAR smart contracts.

Crates.io version Download Join the community on Spectrum Join the community on Discord Travis Build

Features | Pre-requisites | Writing Rust Contract | Building Rust Contract | Running Rust Contract | Limitations and Future Work

Example

Wrap a struct in #[near_bindgen] and it generates a smart contract compatible with the NEAR blockchain: ```rust

[near_bindgen]

[derive(Default, BorshDeserialize, BorshSerialize)]

pub struct StatusMessage { records: HashMap, }

[near_bindgen]

impl StatusMessage { pub fn setstatus(&mut self, message: String) { let accountid = env::signeraccountid(); self.records.insert(account_id, message); }

pub fn get_status(&self, account_id: String) -> Option<String> {
    self.records.get(&account_id).cloned()
}

} ```

Features

Pre-requisites

To develop Rust contracts you would need to: * Install Rustup: bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh * Install the nightly Rust compiler: bash rustup install nightly * Use the nightly compiler for your repo: bash cd ./myproject rustup override set nightly

Writing Rust Contract

You can follow the examples/status-message crate that shows a simple Rust contract.

The general workflow is the following: 1. Create a crate and configure the Cargo.toml similarly to how it is configured in examples/status-message/Cargo.toml; 2. Crate needs to have one pub struct that will represent the smart contract itself: * The struct needs to implement Default trait which NEAR will use to create the initial state of the contract upon its first usage; * The struct also needs to implement BorshSerialize and BorshDeserialize traits which NEAR will use to save/load contract's internal state;

Here is an example of a smart contract struct: rust #[near_bindgen] #[derive(Default, BorshSerialize, BorshDeserialize)] pub struct MyContract { data: HashMap<u64, u64> }

  1. Define methods that NEAR will expose as smart contract methods:

    Here is an example of smart contract methods: ```rust

    [near_bindgen]

    impl MyContract { pub fn insertdata(&mut self, key: u64, value: u64) -> Option { self.data.insert(key) } pub fn getdata(&self, key: u64) -> Option { self.data.get(&key).cloned() } } ```

Building Rust Contract

We can build the contract using rustc: bash RUSTFLAGS='-C link-arg=-s' cargo +nightly build --target wasm32-unknown-unknown --release

Running Rust Contract

If you skipped the previous steps you can use the already built contract from examples/status-message/res/status-message.wasm.

Start the local testnet

Let's start the local Near testnet to run the contract on it.

Note, the locally running node will create testdir directory where it will keep the node state and the configs, including the secret key of the validator's account which we can use to create new accounts later.

Create the project and deploy the contract

Call contract functions

Cleaning up

Limitations and Future Work

The current implementation of wasm_bindgen has the following limitations: * The smart contract struct should be serializable with borsh which is true for most of the structs; * The method arguments and the return type should be json-serializable, which is true for most of the types, with some exceptions. For instance, a HashMap<MyEnum, SomeValue> where MyEnum is a non-trivial tagged-union with field-structs in variants will not serialize into json, you would need to convert it to Vec<(MyEnum, SomeValue)> first. Require arguments and the return type to be json-serializable for compatiblity with contracts written in other languages, like TypeScript; * Smart contract can use std but cannot use wasm-incompatible OS-level features, like threads, file system, network, etc. In the future we will support the file system too; * Smart contracts should be deterministic and time-independent, e.g. we cannot use Instant::now. In the future we will expose Instant::now;

We also have the following temporary inefficiencies: * Current smart contracts do not utilize the trie and do not use state storage efficiently. It is okay for small collections, but in the future we will provide an alternative near::collections::{HashMap, HashSet, Vec} that will be using storage in an efficient way; * The current smart contract size is around typically ~80-180Kb, which happens because we compile-in the bincode and serde-json libraries. In the future, we will cherry-pick only the necessary components from these libraries. For now you can use wasm-opt to slightly shrink the size: bash wasm-opt -Oz --output ./pkg/optimized_contract.wasm ./pkg/contract.wasm See Binaryen for the installation instructions.