100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Functions and Visibility Modifiers

Understand how Solidity functions are declared and the four visibility levels — public, external, internal, and private — plus state mutability keywords like view and pure.

Control Flow & FunctionsBeginner10 min readJul 10, 2026
Analogies

Anatomy of a Solidity Function

A Solidity function declaration bundles several independent concerns into one line: the name and parameters, a visibility specifier (public, external, internal, or private), an optional state-mutability keyword (view, pure, or payable), optional custom modifiers, and an optional returns clause. Unlike many languages, visibility is mandatory — omitting it is a compile error since Solidity 0.5. Getting the visibility right is a security decision, because a function accidentally left public can let anyone call privileged logic.

🏏

Cricket analogy: A team sheet lists each player's role, batting order, and whether they bowl — a Solidity function signature packs name, visibility, and mutability into one compact declaration the same way.

The Four Visibility Levels

public functions can be called both internally and from outside the contract, and the compiler generates a getter for public state variables. external functions can only be called from outside (or via this.f()), and they read arguments directly from calldata, which makes them cheaper for large array parameters. internal functions are callable only within the contract and by contracts that inherit from it — this is the default for functions without an explicit label being disallowed, but internal is the visibility inherited functions rely on. private functions are callable only within the exact contract that defines them and are not visible to derived contracts.

🏏

Cricket analogy: public is like the public gate anyone enters, external the players' entrance from outside only, internal the dressing room shared by the squad and its academy, and private the captain's locked locker.

Rule of thumb: use external for functions meant to be called only from outside (especially with large array/bytes arguments, since calldata is cheaper than memory), public when a function must be callable both internally and externally, internal for shared helpers used by the contract and its children, and private for helpers that should never be overridden or seen by subclasses.

State Mutability: view, pure, and payable

State-mutability keywords declare how a function interacts with the blockchain state. A view function promises not to modify state but may read it; a pure function promises to neither read nor modify state, depending only on its arguments; and a payable function is allowed to receive Ether via msg.value. If none is given, the function may modify state. The compiler enforces these promises: writing to a storage variable inside a view function is a compile error. Calling a view or pure function externally (as an eth_call, not a transaction) costs the caller no gas because nothing is mined.

🏏

Cricket analogy: A view function is like the third umpire reviewing footage — reading the state but never changing the score; a pure function is a run-rate calculator using only the numbers given, touching no live match data.

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Bank {
    mapping(address => uint256) private balances; // private state

    // payable: can receive Ether via msg.value
    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    // view: reads state, never writes it
    function balanceOf(address who) public view returns (uint256) {
        return balances[who];
    }

    // pure: depends only on inputs, touches no state
    function feeFor(uint256 amount) public pure returns (uint256) {
        return (amount * 3) / 1000; // 0.3% fee
    }

    // external: cheaper for calldata; only callable from outside
    function withdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount, "insufficient");
        balances[msg.sender] -= amount; // effects before interaction
        payable(msg.sender).transfer(amount);
    }

    // internal: reusable helper for this contract and its children
    function _netAmount(uint256 amount) internal pure returns (uint256) {
        return amount - feeFor(amount);
    }
}

Marking a function public when it changes privileged state and forgetting to add an access-control modifier is one of the most common critical vulnerabilities. Anyone can call a public function. Restrict sensitive functions with a modifier like onlyOwner, and prefer the least-permissive visibility that still works.

Visibility also interacts with inheritance. A private function cannot be overridden or even seen by a child contract, whereas an internal function can be called and, if marked virtual, overridden by children. Public and external functions form the contract's ABI — the interface other contracts and off-chain clients call. Because external functions are absent from the internal call graph, calling one from within the same contract requires this.f(), which performs an actual message call and costs more gas than an internal call would.

🏏

Cricket analogy: An internal coaching drill can be adapted by the academy team (overridden if virtual), but the captain's private superstition routine stays with him alone and no junior inherits it.

  • Visibility is mandatory in Solidity — every function must be public, external, internal, or private.
  • public: callable inside and outside; external: outside only (cheaper for calldata args); internal: this contract and children; private: this contract only.
  • view reads but never writes state; pure neither reads nor writes; payable can receive Ether via msg.value.
  • The compiler enforces mutability promises — writing state in a view function is a compile error.
  • view/pure calls made as eth_call cost the caller no gas; state-changing calls require a mined transaction.
  • Overly permissive visibility (public without access control) is a top security risk — use the least-permissive level.
  • private functions cannot be overridden or seen by child contracts; internal virtual functions can be overridden.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#SolidityStudyNotes#FunctionsAndVisibilityModifiers#Functions#Visibility#Modifiers#Anatomy#StudyNotes#SkillVeris#ExamPrep