Skip to main content
Smart contracts expose functions so users can interact with them. There are different types of functions including read-only, private, and payable functions.

Contract Interface

All public functions in the contract are part of its interface. They can be called by anyone and are the only way to interact with the contract.
Every public function becomes part of the contract’s ABI (Application Binary Interface) and can be called by external accounts or other contracts.

Initialization Functions

A contract can have an initialization function. If present, this function must be called before any other to initialize the contract state.
use near_sdk::{near, PanicOnDefault};

#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct Contract {
    owner: AccountId,
    data: String,
}

#[near]
impl Contract {
    #[init]
    #[private]
    pub fn init(owner: AccountId) -> Self {
        Self {
            owner,
            data: String::new(),
        }
    }
}
The initialization function is marked with the #[init] macro. The #[derive(PanicOnDefault)] ensures the contract can’t be used without initialization.
It’s a good practice to mark initialization functions as private so only the contract account can initialize the contract.

State-Changing Functions

Functions that modify the state or perform actions need to be called by a user with a NEAR account, since a transaction is required to execute them.
use near_sdk::{near, env};

#[near]
impl Contract {
    // State-changing function takes &mut self
    pub fn set_data(&mut self, data: String) {
        let caller = env::predecessor_account_id();
        near_sdk::log!("Data updated by {}", caller);
        self.data = data;
    }

    pub fn increment_counter(&mut self) {
        self.counter += 1;
    }
}
State-changing functions take a mutable reference to self (&mut self).

Read-Only Functions

Read-only functions don’t modify the state. Calling them is free for everyone and does not require a NEAR account.
#[near]
impl Contract {
    // Read-only function takes &self
    pub fn get_data(&self) -> String {
        self.data.clone()
    }

    pub fn get_counter(&self) -> u64 {
        self.counter
    }

    pub fn get_owner(&self) -> AccountId {
        self.owner.clone()
    }
}
Read-only functions take an immutable reference to self (&self).
View methods have a default gas limit of 200 TGas for execution.

Private Functions

Many times you want functions that are exposed as part of the contract’s interface but should not be called directly by users. Callbacks from cross-contract calls should always be private.
use near_sdk::{near, env, Promise, Gas};

#[near]
impl Contract {
    // Public function that calls external contract
    pub fn external_call(&self) -> Promise {
        ext_contract::ext("external.near".parse().unwrap())
            .with_static_gas(Gas::from_tgas(5))
            .some_method()
            .then(
                Self::ext(env::current_account_id())
                    .with_static_gas(Gas::from_tgas(5))
                    .callback_handler()
            )
    }

    // Private callback function
    #[private]
    pub fn callback_handler(&mut self) {
        // Handle callback result
        near_sdk::log!("Callback executed");
    }
}
Private functions are marked using the #[private] macro in Rust.
Always mark callback functions as private to ensure they can only be called by the contract itself.

Payable Functions

By default, functions will panic if the user attaches NEAR tokens to the call. Functions that accept NEAR tokens must be marked as payable.
use near_sdk::{near, env, NearToken, Promise};

#[near]
impl Contract {
    #[payable]
    pub fn donate(&mut self) -> NearToken {
        let donor = env::predecessor_account_id();
        let amount = env::attached_deposit();

        near_sdk::log!("Received {} from {}", amount, donor);

        // Store the donation
        self.donations.insert(donor, amount);

        amount
    }

    #[payable]
    pub fn purchase_item(&mut self, item_id: u64) {
        let payment = env::attached_deposit();
        assert!(payment >= self.get_item_price(item_id), "Insufficient payment");

        // Process purchase
        self.process_purchase(item_id);
    }
}
Payable functions are marked using the #[payable] macro in Rust.
Within payable functions, use the environment methods to access the attached deposit amount.

Pure Functions

Pure functions don’t require access to the contract state and can return hardcoded values.
const CONTRACT_VERSION: &str = "1.0.0";

#[near]
impl Contract {
    pub fn get_version() -> String {
        CONTRACT_VERSION.to_string()
    }

    pub fn calculate_fee(amount: u128) -> u128 {
        amount / 100  // 1% fee
    }
}
Pure functions don’t access self and can be useful for returning constants or performing calculations.

Function Naming Convention

Best Practice: Use snake_caseWhen naming methods, use snake_case in all SDKs as this is compatible with the remainder of the NEAR ecosystem which is predominantly comprised of Rust contracts.Examples:
  • get_balance()
  • transfer_token()
  • getBalance()
  • transferToken()

Next Steps

Storage

Learn about state management

Cross-Contract Calls

Call other contracts

Testing

Test your functions

Security

Security best practices

Build docs developers (and LLMs) love