near-sdk
Rust library for writing NEAR smart contracts.
Previously known as near-bindgen
.
3.0.1
#[private]
method decorator, that verifies predecessor_account_id() == current_account_id()
.
NOTE: Usually, when a contract has to have a callback for a remote cross-contract call, this callback method should
only be called by the contract itself. It's to avoid someone else calling it and messing the state. Pretty common pattern
is to have an assert that validates that the direct caller (predecessor account ID) matches to the contract's account (current account ID).log!
macro to log a string from a contract similar to println!
macro.test_utils
mod from near_sdk
that contains a bunch of helper methods and structures, e.g.
test_env
- simple test environment mod used internally.testing_env
to be able to pass promise resultsVMContextBuilder
to help construct a VMContext
for testsget_logs
method that returns current logs from the contract execution.env::created_receipts
moved to test_utils::get_created_receipts
.
env
shouldn't contain testing methods.log!
macro#[derive(PanicOnDefault)]
that automatically implements Default
trait that panics when called.
This is helpful to prevent contracts from being initialized using Default
by removing boilerplate code.setup_alloc
macro that generates the same boilerplate as before, but also adds a #[cfg(target_arch = "wasm32")], which prevents the allocator from being used when the contract's main file is used in simulation testing.Base58CryptoHash
and CryptoHash
to represent 32
bytes slice of u8
.LazyOption
to keep a single large value with lazy deserialization.#[init]
now checks that the state is not initialized. This is expected behavior. To ignore state check you can call #[init(ignore_state)]
3.0.0
is not published, due to tag conflicts on the near-sdk-rs
repo.Previous version CHANGELOG
Wrap a struct in #[near_bindgen]
and it generates a smart contract compatible with the NEAR blockchain:
```rust
use nearsdk::{nearbindgen, env};
pub struct StatusMessage {
records: HashMap
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()
}
} ```
Unit-testable. Writing unit tests is easy with near-sdk
:
```rust
fn setgetmessage() { let context = getcontext(vec![]); testingenv!(context); let mut contract = StatusMessage::default(); contract.setstatus("hello".tostring()); asserteq!("hello".tostring(), contract.getstatus("bobnear".to_string()).unwrap()); } ```
Run unit test the usual way:
bash
cargo test --package status-message
Asynchronous cross-contract calls. Asynchronous cross-contract calls allow parallel execution
of multiple contracts in parallel with subsequent aggregation on another contract.
env
exposes the following methods:
promise_create
-- schedules an execution of a function on some contract;promise_then
-- attaches the callback back to the current contract once the function is executed;promise_and
-- combinator, allows waiting on several promises simultaneously, before executing the callback;promise_return
-- treats the result of execution of the promise as the result of the current function.Follow examples/cross-contract-high-level to see various usages of cross contract calls, including system-level actions done from inside the contract like balance transfer (examples of other system-level actions are: account creation, access key creation/deletion, contract deployment, etc).
Initialization methods. We can define an initialization method that can be used to initialize the state of the contract. #[init]
verifies that the contract has not been initialized yet (the contract state doesn't exist) and will panic otherwise.
```rust
impl StatusMessage {
#[init]
pub fn new(user: String, status: String) -> Self {
let mut res = Self::default();
res.records.insert(user, status);
res
}
}
Even if you have initialization method your smart contract is still expected to derive `Default` trait. If you don't
want to disable default initialization then you can prohibit it like this:
rust
impl Default for StatusMessage {
fn default() -> Self {
panic!("Contract should be initialized before the usage.")
}
}
You can also prohibit `Default` trait initialization by using `near_sdk::PanicOnDefault` helper macro. E.g.:
rust
pub struct StatusMessage {
records: HashMap
To declare a payable method simply use #[payable]
decorator:
```rust
pub fn my_method(&mut self) { ... } ```
#[private]
simplifies it, by making it a single line macro instead and improves readability.To declare a private method use #[private]
decorator:
```rust
pub fn my_method(&mut self) { ... } /// Which is equivalent to
pub fn mymethod(&mut self ) { if env::currentaccountid() != env::predecessoraccountid() { nearsdk::env::panic("Method method is private".as_bytes()); } ... } ```
Now, only the account of the contract itself can call this method, either directly or through a promise.
To develop Rust contracts you would need to:
* Install Rustup:
bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
* Add wasm target to your toolchain:
bash
rustup target add wasm32-unknown-unknown
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 use nearsdk::{nearbindgen, env};
#[near_bindgen]
#[derive(Default, BorshSerialize, BorshDeserialize)]
pub struct MyContract {
data: HashMap
Define methods that NEAR will expose as smart contract methods:
&self
, &mut self
, or self
;impl
section with #[near_bindgen]
macro. That is where all the M.A.G.I.C. (Macros-Auto-Generated Injected Code) is happeningenv::*
;Here is an example of smart contract methods: ```rust
impl MyContract {
pub fn insertdata(&mut self, key: u64, value: u64) -> Option
We can build the contract using rustc:
bash
RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release
Since WebAssembly compiler includes a bunch of debug information into the binary, the resulting binary might be different on different machines. To be able to compile the binary in a reproducible way, we added a Dockerfile that allows to compile the binary.
Use contract-builder