External State Mutation
Overview
This is a baseline hint, not a vulnerability finding. For every external, non-view function on a Starknet contract — that is, every public entry point that can modify state — the analysis emits a low-severity informational note. The purpose is to give a reviewer a complete inventory of the contract’s state-changing attack surface in one place, so that each entry point can be checked for correct ordering and access control even when the more specific detectors stay silent.
The hint always fires for qualifying functions and is independent of the deeper checks. Where a specific detector (such as the reentrancy or unvalidated-external checks) finds a concrete problem, that higher-severity finding supersedes this note for triage purposes. Where none fires, the hint still records that the function exists and mutates state, which is exactly the population a manual review should walk through.
Why This Is an Issue
Strictly speaking, an external state-mutating function is not an issue by itself — contracts have to change state somewhere. The value of the hint is coverage. Reentrancy, access-control gaps, and ordering mistakes all live on these functions, and the single most common review error is to miss one entry point entirely. By enumerating every external mutating function, the hint makes that omission visible: if a function appears in the inventory but was never examined for checks-effects-interactions, the review is incomplete.
Treat each hint as a prompt: confirm that the function validates its caller where it should, validates its inputs, and performs all state changes before any external interaction.
How to Resolve
There is nothing to “fix” about the hint itself. The action is to review the named function and confirm it follows safe ordering and access control. The pattern to verify is effects-before-interactions:
// Review target: confirm effects precede interactions and access is checked
#[external(v0)]
fn claim(ref self: ContractState, amount: u256) {
let caller = get_caller_address();
assert(self.eligible.read(caller), 'not eligible'); // checks
self.claimed.write(caller, true); // effects
let token = self.token.read();
token.transfer(caller, amount); // interactions
}
If the review finds the ordering reversed or the access check missing, the corresponding higher-severity detector documents the concrete remediation.
Detection Methodology
The hint pass walks every parsed function and emits a note for each that is external and not a view. It does not inspect the body for specific hazards — that is the job of the dedicated detectors. The pass runs alongside the registry of substantive detectors and contributes one to the count of detectors run, reflecting that it is a distinct, always-on coverage pass rather than a conditional check.
Limitations
- The hint is informational and intentionally noisy on contracts with many entry points; it is meant to be a checklist, not a prioritised alert.
- It does not analyse the function body, so it cannot distinguish a well-guarded entry point from a dangerous one — the specific detectors do that.
- View functions and the constructor are excluded, so state-affecting logic reachable only through those paths is not enumerated here.
Related Detectors
- Cairo Reentrancy (Checks-Effects-Interactions) — storage writes after external calls
- Cairo External Without Validation — external mutating functions that validate no inputs
- Cairo Unchecked Felt Arithmetic — unbounded felt arithmetic feeding state
References
- Cairo Book: Starknet Contracts
- Starknet Documentation: Contract Functions and External Entry Points
- OpenZeppelin Contracts for Cairo
- SWC-107: Reentrancy
Sample Sigvex Output
{
"detector": "external-state-mutation",
"severity": "low",
"confidence": 0.40,
"title": "External function `claim` at line 2 modifies state",
"template": "claim",
"signal": "claim",
"line": 2,
"description": "External function `claim` can modify contract state. Verify that state changes follow checks-effects-interactions pattern to prevent reentrancy attacks.",
"recommendation": "Verify that all external calls happen after state mutations (checks-effects-interactions pattern)."
}