Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Comparisons

Move's `public` vs `friend` vs `entry` Visibility: Module Scoping vs Direct Invocation

A technical deep-dive comparing Move's granular, module-centric function visibility model with Solidity's contract-based approach. This analysis covers security implications, developer experience, and optimal use cases for CTOs and protocol architects.
Chainscore © 2026
introduction
THE ANALYSIS

Introduction: The Paradigm Shift in Smart Contract Access Control

A deep dive into how Move's granular visibility model fundamentally redefines secure composability compared to Solidity's binary public/private paradigm.

Solidity's public/private model excels at developer familiarity and rapid prototyping because it mirrors traditional OOP. For example, the simplicity of marking a function public has enabled the explosive growth of the EVM ecosystem, with over 100 million deployed contracts on Ethereum alone. However, this binary choice creates a critical security-composability trade-off: a public function is globally accessible, while a private one is completely locked, leaving no middle ground for controlled, intra-ecosystem access.

Move's public/friend/entry model takes a different approach by introducing module-scoped visibility and explicit entry points. The friend keyword allows you to whitelist specific modules, enabling secure cross-module calls without exposing functions to the entire network. The entry modifier further restricts functions to be callable only via transactions, not internally. This results in a more secure and intentional architecture by default, as seen in core Aptos and Sui frameworks, but requires more upfront design discipline.

The key trade-off: If your priority is maximizing unrestricted composability and developer speed within a mature EVM toolchain (e.g., for a rapid DeFi prototype on Arbitrum or Base), Solidity's model is effective. If you prioritize security-by-default and granular, verifiable access control for high-value assets or novel financial primitives (e.g., a novel AMM on Aptos or a gaming economy on Sui), Move's friend and entry system provides a fundamentally stronger foundation.

tldr-summary
Move's Visibility Modifiers

TL;DR: Core Differentiators at a Glance

A quick scan of the access control trade-offs between public, friend, and entry functions in the Move language. Choose the right tool for your module's security and composability needs.

01

public(friend) - For Controlled Composability

Granular cross-module access: Functions are only callable by modules explicitly declared in the friend list. This is essential for protocols like Aptos' AMMs or Sui's DeFi packages that need to expose internal logic to a curated set of trusted modules (e.g., a router calling a pool's swap logic) without opening it to the entire network.

02

entry - For Secure Transaction Entry Points

Direct user invocation with validation: Functions are directly callable as transaction scripts, but can enforce complex pre-conditions. This is critical for public-facing operations like NFT minting or token transfers where you need to guarantee atomic, on-chain validation (e.g., checking payment) before state changes occur.

03

public - For Maximum Composability

Global, unrestricted access: Functions are callable by any transaction or module on-chain. This is best for open standards and libraries, like a token's standard transfer function or a widely-used math library, where you want to maximize reuse and network effects without gatekeeping.

04

No Modifier - For Internal Logic

Strict module-private scoping: Functions are only callable from within the same module. This is the default for core security-critical logic and internal helpers, such as a vault's asset rebalancing algorithm or a state validation function, ensuring they cannot be invoked in unintended ways.

MODULE-LEVEL SCOPING VS DIRECT INVOCATION

Feature Matrix: Move Visibility vs Solidity Visibility

Comparison of function and variable access control mechanisms across smart contract languages.

Visibility ModifierMove (Aptos/Sui)Solidity (EVM)

Default Module Scope

private (script-only)

internal (contract-wide)

Cross-Module Call

friend (explicit allowlist)

public (any external contract)

Direct User Invocation

entry (no return, payable)

public (any return, payable)

State Variable Exposure

Explicit public(get) function

Direct public variable access

Inheritance Impact

No inheritance, uses modules

Inherits visibility, can override

External Call Gas Cost

Lower (module-local optimization)

Higher (external call overhead)

pros-cons-a
Module Scoping vs Direct Invocation

Move's Visibility Model: Pros and Cons

A technical breakdown of Move's public, friend, and entry function modifiers, highlighting their security trade-offs and performance implications for smart contract design.

01

`public` Functions: Maximum Composability

Global accessibility: Functions are callable by any other module or transaction. This enables maximum composability for protocols like DeFi lending markets (e.g., Aave's port) where any contract must interact with core logic. However, it expands the attack surface, requiring rigorous formal verification.

02

`friend` Functions: Controlled Module Access

Explicit allow-listing: Functions are only callable by modules declared in the friend list. This is critical for secure internal APIs within a protocol suite (e.g., Aptos' liquid staking), limiting cross-module dependencies and preventing unauthorized state mutations from external contracts.

03

`entry` Functions: Direct User Invocation

Transaction entry point: Functions are directly callable by end-users, bypassing module encapsulation. This provides lower latency and gas costs for common actions (e.g., Sui's transfer). The trade-off is that entry functions cannot return values, requiring event-driven or state-query patterns.

04

The Security vs Flexibility Trade-off

Strict by default: Move's model enforces explicit access control, reducing reentrancy and unauthorized access risks prevalent in Solidity. The cost is reduced ad-hoc composability; you cannot 'plug and play' with arbitrary contracts like on Ethereum without pre-declared friend relationships, which can slow ecosystem integration.

pros-cons-b
MODULE SCOPING VS DIRECT INVOCATION

Move's Visibility Model: Pros and Cons

A technical breakdown of Move's public, friend, and entry modifiers, contrasting them with Solidity's model to highlight architectural trade-offs.

01

Pro: Granular Access Control with `friend`

Explicit cross-module whitelisting: Unlike Solidity's internal (same contract) or public (everyone), friend allows a function to be called only by modules explicitly declared in its friend list. This enforces a least-privilege architecture, critical for secure DeFi protocols like Aptos Liquidswap or Sui DeepBook where specific internal orchestration is required.

02

Pro: Transaction-Bound Safety with `entry`

Enforced transaction entry points: The entry modifier restricts a public function to be only callable as the initial transaction script. This prevents internal reentrancy by design and creates a clear boundary between user-invokable actions and internal logic. This is a structural advantage over Solidity, where any public function can be called mid-execution, a common vulnerability vector.

03

Con: Increased Development Overhead

Explicit dependency management required: The friend system forces developers to pre-declare all inter-module dependencies. While secure, this adds friction during rapid iteration and refactoring compared to Solidity's simpler internal/external model. Migrating large codebases (e.g., from Ethereum to Aptos) requires significant architectural re-wiring to map existing access patterns.

04

Con: Limited Composability Patterns

Restricts fluid integration: The strict module boundaries and entry constraints can hinder certain composability patterns common in Ethereum's DeFi ecosystem. For example, a flash loan aggregator cannot directly call an internal liquidity function marked as friend-only to another module. This pushes complexity into transaction scripts or requires more upfront design, unlike Solidity's more permissive public functions.

CHOOSE YOUR PRIORITY

When to Choose: Developer Personas and Use Cases

friend for Security-First Apps

Verdict: The essential tool for secure, modular design. Strengths: Enforces a strict, auditable trust boundary. Critical logic (e.g., a lending protocol's liquidation engine) can be encapsulated in a module, with friend functions allowing only vetted, internal modules (like a RiskManager) to call it. This prevents arbitrary external invocation, drastically reducing attack surface. It's superior to Solidity's internal as it's cross-module, not just contract-wide. Use Case: Building a secure DeFi protocol like Aave or Compound on Aptos/Sui, where core financial logic must be protected from public access but needs orchestration by other system modules.

public for Security-First Apps

Verdict: Use sparingly for well-defined, safe entry points. Trade-off: Any public function is a permanent API commitment and a potential vulnerability if not rigorously validated. Use only for actions that are inherently safe for any user to invoke, like querying a price from an oracle module.

entry for Security-First Apps

Verdict: The secure public face. Combines public visibility with transaction-level atomicity, preventing mid-function reentrancy. Mandatory for any function that modifies global state and should be callable from external transactions.

MOVE VISIBILITY MODIFIERS

Technical Deep Dive: Module Scoping vs Direct Invocation

Understanding the nuanced access control between `public`, `friend`, and `entry` functions is critical for designing secure and composable Move modules. This deep dive compares their scoping rules, gas implications, and ideal use cases for protocol architects.

public functions allow cross-module calls, while entry functions are directly invocable as transaction entry points. A public function can be called by any other function within the same module or in a different module, enabling internal logic reuse. An entry function is a special type of public function that can be the target of a user-signed transaction, like aptos_account::transfer. You can have a public function that is not entry, but all entry functions are implicitly public.

Example:

move
public fun internal_logic() { ... } // Can be called by other functions
public entry fun user_transaction() { ... } // Can be a transaction target
verdict
THE ANALYSIS

Final Verdict and Decision Framework

Choosing between `public`, `friend`, and `entry` visibility in Move is a strategic decision balancing security, composability, and user experience.

public functions excel at enabling broad, permissionless composability because they are callable from any module. For example, core DeFi protocols like Aptos Liquidswap and Sui Turbos Finance rely on public entry points for their automated market makers, allowing any user or smart contract to interact with swap pools directly. This maximizes protocol utility and ecosystem integration but exposes a larger attack surface, requiring rigorous auditing of every public function.

friend functions take a different approach by enabling controlled, module-level composability. This results in a trade-off between security and flexibility. A module can declare other modules as friends, granting them exclusive access. This is ideal for upgradeable contracts or internal protocol logic, as seen in Aptos Framework upgrades, where only the governance module can call critical migration functions. It prevents unauthorized external calls but adds administrative overhead in managing friend lists.

entry functions are optimized for direct user and script invocation, acting as transaction entry points. This design results in superior user experience and gas efficiency for end-users, as they can call these functions without intermediate wrapper contracts. On Sui, which heavily promotes entry, this leads to simpler dApp interfaces and measurable reductions in transaction complexity. However, entry functions cannot be called by other Move modules, limiting internal composability.

The key architectural trade-off: If your priority is maximizing ecosystem composability and protocol utility, default to public functions. If you prioritize secure, upgradeable internal logic with controlled access, architect with friend visibility. For user-facing operations where direct invocation and gas efficiency are paramount, design your transaction endpoints as entry functions. Most production systems, like those on Aptos and Sui, use a hybrid model, leveraging each modifier for its intended scope.

ENQUIRY

Get In Touch
today.

Our experts will offer a free quote and a 30min call to discuss your project.

NDA Protected
24h Response
Directly to Engineering Team
10+
Protocols Shipped
$20M+
TVL Overall
NDA Protected Directly to Engineering Team