Generally follows the Solidity Style Guide with a few exceptions.
By default, running make format
will run the formatter forcing conformation to most of the style guide.
Ideally, each function argument should fall on its own line. For example:
thisFunctionCallIsReallyLong(
longArgument1,
longArgument2,
longArgument3
);
For the time-being, default make format
line-breaking is acceptable for uniformity. This is based on a fixed line length of 120
characters instead.
Sort imports by length.
// Bad
import {Registry} from "./Registry.sol";
import {Token} from "./Token.sol";
import {BaseConsumer} from "./consumer/Base.sol";
// Good
import {Token} from "./Token.sol";
import {Registry} from "./Registry.sol";
import {BaseConsumer} from "./consumer/Base.sol";
Ordering of sections in contract goes:
- SPDX identifier
- Imports
- Public structs
- Contract definition
- Structs
- Enums
- Constants
- Immutable variables
- Mutable variables
- Events
- Errors
- Modifiers
- Constructor
- Internal functions
- External functions
- Fallback function
Sections are delineated using headers generated from transmissions11/headers.
- Always wrap
if
statements in closures - Do not use one-line statements
// Bad
if (value == 0) return 1;
// Good
if (value == 0) {
return 1;
}
Always explitly return variables from functions.
// Bad
function test() external returns (uint256 myVar) {
myVar = 1;
}
// Good
function test() external returns (uint256) {
myVar = 1;
return myVar;
}
Prefer private
> internal
> external
> public
, to best discretion.
- Prefer
camelCase
. - For constants, immutables, and values that should act as constants or immutables (in cases where you can't initialize as such, like tests), prefer
ALL_CAPS_CONSTANTS
. - Function names should start with imperative verbs
- Functions with
internal
visibility should be prefixed with an underscore:_internalFunction
. This is not necessary for variables
- Explicitly declare variable sizes (no
uint
) - Prefer smallest variable size by default (eg
uint32
for timestamp overuint256
), unless in special cases where casting overhead is expensive or readability impact of switching is low - Declare in order
type visibility mutability name
, egaddress internal immutable myContractAddress
- Prefer typed contract inheritance (eg
Type myContract
overaddress myContractAddress
) - Ignore (2) when setting up developer-facing interfaces. It's easier for developers to not have to worry about the initialization.
Use custom errors.
- Choose to emit events liberally.
- Think through what data would be useful to index off-chain when determining event parameters.
- Generally, avoid unless significant optimizations possible
- Comment all memory layouts
- Use only
memory-safe
assembly; mark assembly blocks as such
- Prefer two-step processes (
ownership
transfer, joining as a node) over one step to reduce chance for human error