AppStorage for distinguishing state variables and preventing name clashes

Solidity state variables, immutable variables, constants, local variables and function names can look the same and it is possible to have name clashes, where for example a local variable or function name is named the same thing as a state variable, which can reduce code clarity and cause frustration.

AppStorage is a variable naming technique which makes state variables clear in your code and prevents name clashes with other kinds of variables and function names.

This is how it is done: Define a struct in a file and name the struct AppStorage. Define all the state variables in your application within the AppStorage struct.

Then in your smart contracts import the AppStorage struct. Then define this state variable: AppStorage internal s;.

That’s it. Now you can access all your state variables by prepending an “s.” to them.

Here is a simple example:

  1. Define AppStorage that contains all your state variables:
// AppStorage.sol
struct AppStorage {
  uint256 secondVar;
  uint256 firstVar;
  uint256 lastVar;
  ...
}
  1. Import it and use it in contracts:
import "./AppStorage.sol"

contract Staking {
  AppStorage internal s;

  function myFacetFunction() external {
    s.lastVar = s.firstVar + s.secondVar;
  }

If you have name clashes between variables, this should already be being flagged by the Solidity compiler as This declaration shadows an existing declaration. If you have vscode-solidity set up, your attention will be drawn to such warnings as you type.

Nevertheless, your scheme would still be useful to distinguish between storage and non-storage variables, as even if a storage variable’s name does not clash with any other, it is still useful to be aware that you are accessing a storage variable, as this could consume non-negligible gas. In a similar vein, in the past I have considered simply naming the storage variables $firstVar, $secondVar, etc. but in the end I abandoned the scheme because if you need the storage variables to be public, then this “implementation detail” (the fact that they are storage variables) will “leak” to your contract’s public interface, and you might not want that. Fine, that could be mitigated by exposing selected members of the internal s with explicit external getters… but it’s more boilerplate. In any case, if your scheme works for your scenario, then that’s great. Adding the extra level of s. indirection shouldn’t result in higher execution gas, but I’d still check just in case.

Frankly I believe that the ideal place to address this issue would be as a PR to vscode-solidity (or would the correct place be in the Solidity language server?) that would render different types of variable (storage, memory, local, calldata, etc.) in different ways and/or provide further info about the variable-type on hover.

We have a planned feature that would let you avoid boilerplate in this case: Ability to override function selectors to support external interfaces not expressible in valid syntax. · Issue #11819 · ethereum/solidity · GitHub

The idea is to allow specifying function ABI signatures separately from their in-language names. In case of public state variables this would let you have a different name for the generated getter and the underlying variable. This is how it would look like in the syntax I proposed there:

contract C {
    uint public "value" as s_value;
}

The issue is currently stuck in the design backlog and needs more input regarding syntax (the form I’m showing above is unfortunately not very popular) so if you think it would be a good solution for your problem, any feedback is welcome to push it forward.

Thanks @cameel. The syntax is admittedly slightly odd :sweat_smile: IMO my particular use case would not justify a language extension, but I am going to be following the discussion in order to understand better the rationale behind the planned feature :slight_smile: Thanks again.

This would not be the main use case of course. We need it mainly because when we add new keywords, users can no longer use them as function names. Those names could still exist in the ABI. We recently had this dilemma with error.

Syntax is actually the main blocker for now so if you have some good ideas, please post in the issue :slight_smile: There are some more proposals in addition to what I have shown above. They have some advantages (e.g. the {} syntax resembles call options), but in my opinion readability is not great and they don’t work that well for state variables, which do not have the function keyword and where the = already has a meaning.

function{ signature = "error()" } errorCompat() external returns (uint);
function errorCompat() external returns (uint) = "error()";
function "error()"() external;