Near Learn

Accounts: who is calling

address becomes AccountId — a human-readable name. predecessor, signer and current account replace msg.sender, tx.origin and address(this).

Beginner4 min read3-question check

On the EVM an account is a 20-byte hash. On NEAR it is a named account — alice.near, app.near — and there are three context accounts to keep straight where Solidity has msg.sender, tx.origin and address(this).

The three context accounts
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Who {
    function caller() external view returns (address) {
        return msg.sender;
    }

    function origin() external view returns (address) {
        return tx.origin;
    }

    function self_() external view returns (address) {
        return address(this);
    }
}
Rust
use near_sdk::{near, env, AccountId};

#[near(contract_state)]
#[derive(Default)]
pub struct Who {}

#[near]
impl Who {
    // the immediate caller (a user, or a calling contract)
    // &mut self: predecessor/signer only exist in a transaction (change call)
    pub fn caller(&mut self) -> AccountId {
        env::predecessor_account_id()
    }

    // the account that signed the transaction
    pub fn origin(&mut self) -> AccountId {
        env::signer_account_id()
    }

    // this contract's own account — fine in a view call too
    pub fn this(&self) -> AccountId {
        env::current_account_id()
    }
}
Coming from EVM

address ↔ AccountId: a validated name like alice.near, not a hash. You compare and clone it; there is no address arithmetic.

msg.sender ↔ env::predecessor_account_id() — the immediate caller. In a cross-contract call the predecessor is the *calling contract*, exactly like msg.sender.

tx.origin ↔ env::signer_account_id() (the account that signed). address(this) ↔ env::current_account_id(). Accounts are hierarchical: app.near can create sub.app.near, which matters for keys and storage.

Unlike Solidity, where msg.sender works inside a view function, predecessor_account_id() and signer_account_id() exist only in change calls (signed transactions). A free near view call has no caller, so they panic as "prohibited in view" — that is why caller and origin take &mut self and must be invoked with near call. current_account_id() works in both.

Check yourself

3 questions · progress saved in this browser

  1. 1.Solidity's msg.sender is the immediate caller. What is the NEAR equivalent?
  2. 2.Alice signs a transaction that calls contract A, and A calls contract B. Inside B, who are the predecessor and the signer?
  3. 3.How is a NEAR account identified, compared with an EVM address?