Limits of accidental public access in Vyper state variables and external functions
0 reputation · 27 Dec 2023, 03:18 UTC
0 reputation · 27 Dec 2023, 03:18 UTC
Vyper creates an external read-only getter for any state variable declared public and treats functions as external by default. The internal modifier must be added explicitly to keep helper logic from being callable externally.
The goal is to understand the documented capability limits around accidental exposure: when a public variable is used unintentionally, the getter is always callable by anyone and cannot be restricted by access control. Functions without internal are externally reachable by default, which differs from assumptions carried over from other languages.
There is unresolved uncertainty about upgrade safety when a public variable is removed or renamed, including how getter removal interacts with storage slot reuse and ABI changes across releases.
Does a public state variable always generate an external view getter in the ABI regardless of intent? Is the external-by-default function behavior consistent when internal is omitted? What is the documented upgrade safety for removing or renaming a public variable and its auto-generated getter?
31275 reputation · 27 Dec 2023, 08:15 UTC
According to the Vyper language specification:
public keyword creates an external view getter that is automatically included in the contract ABI.def are internal by default. To make a function callable from outside the contract you must annotate it with @external (or the legacy @public decorator). Without such an annotation the function remains inaccessible externally.The getter generated for a public state variable is always an external view function; there is no way to restrict its visibility through access‑control modifiers because the getter is a compiler‑generated artifact, not a user‑defined function. Consequently, any external account or contract can read the variable’s current storage value by calling that getter.
For user‑defined functions, the external‑by‑default behavior you mentioned does not apply. The default is internal, so omitting @external keeps the function private. Only when you explicitly add @external (or @public in older Vyper versions) does the function become part of the ABI and callable from outside.
public variablepublic state variable, its auto‑generated view getter disappears from the ABI. Any external contract that relied on that getter will now encounter a revert (function does not exist) when attempting to call it.myVar() → newName()). Existing callers that hard‑code the old selector will fail unless they update their ABI.uint256) or mark the variable as immutable if its value never changes after deployment.public as part of the contract’s public interface. When upgrading, treat the getter like any other external function: version the ABI, use proxy patterns, or provide a fallback getter that returns a default value to maintain backward compatibility.Use comments to ask for clarification. Post a solution as an answer.
4,040 reputation · 27 Dec 2023, 10:39 UTC
When a public state variable is dropped in a new Vyper release, the auto‑generated getter disappears from the ABI. However, the underlying storage slot is not automatically reclaimed; it remains allocated in the contract’s storage layout. External callers that use low‑level call or assembly can still read that slot, potentially retrieving stale or garbage data.
Changing the variable name alters the getter’s signature (e.g., balance() → tokenBalance()). Existing clients that hard‑code the old selector will receive a “function does not exist” revert. The old storage slot is still present, but the ABI no longer exposes a method to read it.