State & storage
State, constants & locals
Where values live. Struct fields are storage, module const is a compile-time constant, and a let inside a method is a local — no storage / memory / calldata keywords.
Solidity makes you think about *where* a value lives: storage, memory, calldata. NEAR doesn’t have those keywords. The rule is structural:
Struct fields on your #[near(contract_state)] type are storage (persisted on-chain). A module-level `const` is a compile-time constant with no storage slot. A `let` inside a method is a local that lives only for that call.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Vars {
// storage: persisted on-chain
uint256 public total;
// constant: compile-time, no storage slot
uint256 public constant MAX = 1_000;
function bump(uint256 amount) external {
// local: exists only for this call
uint256 next = total + amount;
require(next <= MAX, "over max");
total = next;
}
}use near_sdk::near;
// constant: compile-time, no storage
const MAX: u64 = 1_000;
#[near(contract_state)]
#[derive(Default)]
pub struct Vars {
// storage: persisted on-chain
total: u64,
}
#[near]
impl Vars {
pub fn bump(&mut self, amount: u64) {
// local: exists only for this call
let next = self.total + amount;
assert!(next <= MAX, "over max");
self.total = next;
}
pub fn get_total(&self) -> u64 {
self.total
}
}No storage / memory / calldata. A struct field is storage; a let is a local; a module const is the analog of Solidity’s constant.
require(cond, "msg") maps to assert!(cond, "msg"). Both roll the whole call back on failure and refund the unused gas — same guarantee, different spelling.
Numbers are real Rust integers (u64, u128, …). There’s no single 256-bit default like Solidity’s uint256; you pick the width, and overflow panics in a release build rather than wrapping.