Using Rust to create NEAR Contracts
Rust is more mature and complex toolchain than Assembly Script for creating smart contracts.
We suggest that you start learning Smart Contracts with Assembly Script first.
If you want to learn Rust or you already have some experience with Rust, you can explore the possibilities of the NEAR Rust SDK
Make sure you have near-cli installed to be able to deploy the contract.
npm install -g near-cli
For installing the Rust Toolchain on your machine, please follow the instructions from these links:
Official Rust Installation
NEAR Rust SDK Guide
In linux & macOS you can use this command
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Then install the WASM toolchain
rustup target add wasm32-unknown-unknown
Create a folder to contain the Rust contract.
For example: /contract/rust
cd /contract/rust
cargo init --lib
Then add near-sdk-rs to your project in the newly created Cargo.toml
...
[lib]
crate-type = ["cdylib"]
[dependencies]
near-sdk = "4.0.0-pre.7"
[profile.release]
codegen-units = 1
# Tell `rustc` to optimize for small code size.
opt-level = "z" # Experiment with 's' or 'z' to see which produces a smaller bundle
lto = true
debug = false
panic = "abort"
# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801
overflow-checks = true
# rustflags = ["-C link-arg=-s"]
Then some sample code to your /src/lib.rs
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::near_bindgen;
#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
pub struct Contract {
// SETUP CONTRACT STATE
counter: u8
}
#[near_bindgen]
impl Contract {
// ADD CONTRACT METHODS HERE
pub fn increment_counter(&mut self) {
self.counter += 1;
}
pub fn get_counter(&self) -> u8 {
self.counter
}
}
Then you can compile your Rust contract.
cargo build --target wasm32-unknown-unknown --release
Get your wasm file from the target/wasm32-unknown-unknown/release
folder. The file name
copy target\wasm32-unknown-unknown\release\[project_name].wasm contract.wasm
First you need to have a NEAR account and log into it with near-cli.
near login
# Follow the instructions
# Expect to see "Logged in as [ xxx.testnet ] with public key [ ed25519:7SsY83... ] successfully"
Make sure you have enough NEAR tokens to pay for the transaction.
Then you can deploy your Contract to NEAR.
near deploy xxx.testnet contract.wasm
# Expect to see "Done deploying to xxx.testnet"
You can check the status of your deployment by visiting your NEAR wallet
NEAR Wallet
NEAR Testnet Wallet
If you see the transaction,
Congratulations you have deployed your first NEAR Rust contract!
To increment the counter you can call:
near call [Contract Account] increment_counter --accountId [Your Account]
# Expect to see "Counter Increased to 1"
You can check the official NEAR Rust Puzzle Example
Or have a look the the Search NEAR Rust Contract
This contract is calling other NEAR Contract methods as request by the Search Near users.
You can see we are supplying arguments (account_id, method_name and arguments)
We are also accessing environment variables (predecessor_account_id, current_account_id, attached_deposit)
TIP
The annotation "#[payable]" is mandatory for NEAR Rust Contract Methods that can accept deposits
Initially we are preventing infinite loops by checking the predecessor !== current account.
Then we are verifying that there is enough balance to pay for the transaction and fee.
Then we are calling the method on the other contract.
Then we are returning the result of the method.
```rust
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::{env, near_bindgen, AccountId, Promise};
#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
pub struct NearSearch;
#[near_bindgen]
impl NearSearch {
#[payable]
pub fn call_contract(&mut self, account_id: AccountId, method_name: String, args: String) {
let predecessor = env::predecessor_account_id();
let current = env::current_account_id();
if predecessor == current {
env::log("Cannot \"call_contract\" from \"call_contract\"".as_bytes());
return;
}
// this is 0.01 NEAR
const tax: u128 = 10_000_000_000_000_000_000_000;
let mut amount = env::attached_deposit();
if amount > tax {
Promise::new(current).transfer(tax);
amount -= tax;
}
let gas = env::prepaid_gas();
let promise_index = env::promise_create(
account_id,
method_name.as_bytes(),
args.as_bytes(),
amount,
gas/2 // A more accurate split of the gas between the functions is preferable
);
env::promise_return(promise_index);
}
}