Patterns
Ownership
No OpenZeppelin Ownable base to inherit — you keep an owner field and a guard, plus a two-word transfer method.
The Ownable pattern is everywhere in Solidity, usually inherited from OpenZeppelin. On NEAR there is no base contract to inherit; you keep an owner field, guard the privileged methods, and expose a transfer method.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
abstract contract Ownable {
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}
function transferOwnership(address newOwner) external onlyOwner {
owner = newOwner;
}
}use near_sdk::{near, env, require, AccountId};
#[near(contract_state)]
pub struct Owned {
owner: AccountId,
}
#[near]
impl Owned {
#[init]
pub fn new(owner: AccountId) -> Self {
Self { owner }
}
pub fn owner(&self) -> AccountId {
self.owner.clone()
}
pub fn transfer_ownership(&mut self, new_owner: AccountId) {
require!(env::predecessor_account_id() == self.owner, "not owner");
self.owner = new_owner;
}
}No inheritable Ownable. You hold the owner AccountId and guard with require!(predecessor == owner) — the access-control pattern from earlier, applied to ownership.
Remember cross-contract semantics: predecessor_account_id() is whoever called *this* method. If a DAO or multisig owns the contract, the owner is *that contract’s* account, and it calls you like any other caller.
For a safer handoff, add a two-step transfer (propose then accept) so a typo in new_owner can’t brick control — the same reason OpenZeppelin ships Ownable2Step.