Summary
The program { ... } block is the externally-accessible surface (the on-chain ABI): entry fn, view fn, records, mappings, and storage all live inside it because they are externally callable/queryable/readable. helper fn and final fn are inlined and never independently callable, so they are declared outside the block.
A top-level final fn, though it sits outside the block, can still name program-interior mappings and storage variables directly, unqualified.
final fn update_balance(receiver: address, amount: u64) {
let current: u64 = balances.get_or_use(receiver, 0u64); // `balances` is declared inside program { ... }
balances.set(receiver, current + amount);
}
program cheatsheet.aleo {
mapping balances: address => u64;
// ...
}
So the braces gate external access but do not gate name visibility — two different meanings layered on one set of braces.
Proposed fix
A final fn is really a helper function that happens to permit on-chain code. Treat it like one: require it to qualify mapping/storage references with the program name, e.g.
final fn update_balance(receiver: address, amount: u64) {
let current: u64 = cheatsheet.aleo::balances.get_or_use(receiver, 0u64);
cheatsheet.aleo::balances.set(receiver, current + amount);
}
This makes the dependency on program-interior state explicit at the point of use and keeps the program { ... } braces meaning exactly one thing.
Summary
The
program { ... }block is the externally-accessible surface (the on-chain ABI): entryfn,view fn, records, mappings, and storage all live inside it because they are externally callable/queryable/readable.helper fnandfinal fnare inlined and never independently callable, so they are declared outside the block.A top-level
final fn, though it sits outside the block, can still name program-interior mappings and storage variables directly, unqualified.final fn update_balance(receiver: address, amount: u64) { let current: u64 = balances.get_or_use(receiver, 0u64); // `balances` is declared inside program { ... } balances.set(receiver, current + amount); } program cheatsheet.aleo { mapping balances: address => u64; // ... }So the braces gate external access but do not gate name visibility — two different meanings layered on one set of braces.
Proposed fix
A
final fnis really a helper function that happens to permit on-chain code. Treat it like one: require it to qualify mapping/storage references with the program name, e.g.final fn update_balance(receiver: address, amount: u64) { let current: u64 = cheatsheet.aleo::balances.get_or_use(receiver, 0u64); cheatsheet.aleo::balances.set(receiver, current + amount); }This makes the dependency on program-interior state explicit at the point of use and keeps the
program { ... }braces meaning exactly one thing.