What is the difference between entry and public functions in Sui Move?
November 4, 2026
A public function can be called from other modules, other packages and directly from a transaction, so it is the default way to expose functionality. entry is not a visibility level: it is a modifier that makes a non-public (private or public(package)) function callable as a transaction command while keeping it out of other packages' reach. In exchange, the arguments to a non-public entry function are statically checked so they can't be tangled up with an unresolved hot potato from earlier in the same transaction.
Side by side
module book::shop;
// Callable from transactions, other modules and other packages.
public fun buy(/* ... */) { }
// Callable only as a transaction command (and from this module).
entry fun withdraw_all(/* ... */) { }
// Callable from this package and from transactions, not other packages.
public(package) entry fun admin_reset(/* ... */) { }
// Callable only inside this module.
fun helper() { }| Other packages can call it | Transaction can call it | Arguments checked for hot potatoes | |
|---|---|---|---|
public fun |
yes | yes | no |
public(package) fun |
no (same package only) | no | n/a |
entry fun (private) |
no | yes | yes |
public(package) entry fun |
no (same package only) | yes | yes |
fun (private) |
no | no | n/a |
Both public and non-public entry functions can be called from a programmable transaction block or with sui client call.
When to use each
Use public for almost everything. A public function can be a step in a PTB, combined freely with other commands, and used as a building block by other packages. The Move Book calls this "the default - and recommended - way to expose functionality".
Use a non-public entry function when you deliberately want to limit composability:
- The function should only ever be a top-level transaction command, never wrapped by someone else's package.
- You want the guarantee about arguments described below.
Keep in mind that public is a long-term commitment: package upgrades must keep existing public function signatures unchanged, while non-public signatures, including entry and public(package) ones, can change. See package upgrades.
The hot potato guarantee
A hot potato is a value with neither store nor drop, such as a flash-loan receipt that must be repaid before the transaction ends. Since Sui v1.62, the arguments to a non-public entry function must not be "entangled" with any hot potato still outstanding in the transaction. The check runs before execution; a transaction that breaks it fails verification and isn't executed.
Concretely, if a PTB borrows a Balance from a flash lender, turns it into a Coin, and passes that coin to your private entry fun spend(coin: &mut Coin<SUI>) before the loan is repaid, the transaction is rejected. Repay first, and the same call is valid. Taking a shared object by value also taints the values used with it, so a non-public entry function can't take anything that previously interacted with a by-value shared object in that transaction.
public entry is redundant
Older code often uses public entry fun. Today entry adds nothing to a public function, and the compiler warns about it:
warning[Lint W99010]: unnecessary `entry` on a `public` functionDrop entry from public functions, or drop public if you meant to restrict callers.
Other details
entryplaces no restrictions on the function's signature; any Move function can be markedentry.initcan't beentry(see how init works).- Test code (
#[test]and#[test_only]) can callentryfunctions regardless of their visibility.
Sources
- Visibility Modifiers, The Move Book
- Entry Functions, The Move Book
- Upgrade packages, Sui documentation
- Unit Testing, Move Reference