NEAR Smart Contracts in RUST – Best Practices

(66 nL)
2 min read
Learn NEAR Club banner
To Share and +4 nLEARNs

Init once

Make sure you check that state doesn’t exists inĀ initĀ using

assert!(!env::state_exists(), "The contract is already initialized");

Prevent Default

By defaultĀ near_sdkĀ allows contract to be initialized with default state. Usually, if you have a constructor, you want to prevent default state initialization.

impl Default for Contract {
    fn default() -> Self {
        env::panic(b"The contract should be initialized before usage")
    }
}

Public vs private methods

pub fnĀ makes a method public and exposes it in a contract. It means anyone can call it.Ā fnĀ makes the method private and it’s not exposed from the contract. No one can call it directly. It can only be called within a contract directly (not through a promise).

Private callbacks

If you’re using callbacks, makes sure you check the caller.

assert_eq!(env::current_account_id(), env::predecessor_account_id(), "Can only be called within a contract");

Callbacks have to be public methods.

JSON types

NEAR currently expects contracts to support JSON serialization. JSON can’t handle large integers (above 2**53 bits). That’s why you should use helper classes from theĀ json_typesĀ inĀ near_sdkĀ forĀ u64Ā andĀ u128. We provide typesĀ U64Ā andĀ U128, that wraps the integer into a struct and implements JSON serialization and deserialization as a base-10 strings.

E.g.

pub fn add(&self, a: U128, b: U128) -> U128 {
    (a.0 + b.0).into()
}

View vs Change method

By default,Ā near_sdkĀ assumes that the method is aĀ viewĀ if it hasĀ &selfĀ and method isĀ changeĀ if it hasĀ &mut self. View methods usually don’t have ability to change state and have limited context. Change methods will save the modified state back to the contract.

STATEĀ storage key

The contract will save the main structure state under the storage keyĀ STATE. Make sure you don’t modify it e.g. through collection prefixes or raw storage access.

Enable overflow checks

It’s usually helpful to panic on integer overflow. To enable it, add the following into yourĀ Cargo.tomlĀ file:

[profile.release]
overflow-checks = true

UseĀ assert!Ā early in the method.

Try to validate the input, context, state and access first before making any actions. This will safe gas for the caller to fail earlier.

UseĀ env::log

Use logging for debugging and notifying user. When you need a formatted message, you can use the following:

env::log(format!("Transferred {} tokens from {} to {}", amount, sender_id, receiver_id).as_bytes());

ReturnĀ Promise

If your method makes a cross-contract call, you may want to return theĀ PromiseĀ that the contract creates. It allows the caller to wait for the result of the promise instead of the returning the result immediately. Because if the promise fails for some reason, the caller will not know about this or the caller may display the result earlier.

E.g.

pub fn withdraw(&mut self, amount: U128) -> Promise {
    Promise::new(self.owner_id.clone()).transfer(amount.0)
}
Generate comment with AI 2 nL
113

Leave a Comment


To leave a comment you should to:


Scroll to Top
Report a bugšŸ‘€