Initialization (constructors)
When default state is not enough, mark a method #[init]. It runs once, takes arguments, and returns the starting state — the constructor.
Beginner4 min read3-question check
The contracts so far used #[derive(Default)] and initialized themselves lazily. When the starting state depends on arguments — an owner, a cap, a token address — you need a real constructor. On NEAR that is a method marked #[init] that returns Self.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Vault {
address public owner;
uint256 public cap;
constructor(address _owner, uint256 _cap) {
owner = _owner;
cap = _cap;
}
}use near_sdk::{near, AccountId, PanicOnDefault};
use near_sdk::json_types::U128;
#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct Vault {
owner: AccountId,
cap: u128,
}
#[near]
impl Vault {
#[init]
pub fn new(owner: AccountId, cap: U128) -> Self {
Self { owner, cap: cap.0 }
}
pub fn owner(&self) -> AccountId {
self.owner.clone()
}
}constructor(...) ↔ a method marked #[init] that returns Self. near-sdk 5.x requires the state struct to either implement Default or #[derive(PanicOnDefault)]. A contract *can* have both Default and an #[init] (methods then fall back to the default state until init runs); deriving PanicOnDefault instead is how you say "there is no sensible default — init is mandatory".
With PanicOnDefault, the #[init] method must be called once after deploy, before anything else. Until it runs the contract has no state and every other method panics ("The contract is not initialized") — deploy and init are two separate steps (or one, if you deploy with an init call).
U128 / U64 (near_sdk::json_types) are string-wrapped integers for method arguments and returns. JSON numbers lose precision above 2^53, so money-sized values cross the ABI as U128 and you read the inner value with .0.
Check yourself
3 questions · progress saved in this browser