- We write our contracts in the Solidity programming language.
- We build, test, format, and deploy the contracts with Foundry.
- We adhere to most best practices mentioned in the Foundry Book, including:
- Importing specific symbols instead of entire Solidity files
- External dependencies are imported first, then “src”, then “test”
- Code is formatted using
forge fmt
- Each test contract serves as a “describe” block to test a function
- We follow a naming style that is consistent throughout the codebase:
- Contracts, interfaces, and libraries are in
PascalCase
- Constants are in
SNAKE_CASE
- Functions and variables and are in
camelCase
- Internal and private functions and variables start with
_
- To avoid shadowing state variables, function parameters have a trailing
_
- Errors follow the convention SablierV2<ContractName>_<ErrorName>
- Test functions follow the convention
test(Fork)?(Fuzz)?_(RevertWhen_){1})?\\w{1,}
; a comprehensive list of valid and invalid examples can be found here
- Directories are in
kebab-case
- File name are in either
PascalCase
or camelCase
- Test file name end in
.t.sol
, while scripts end in .s.sol
- We adhere to the Checks-Effects-Interactions patterns (to the extent possible).
- We lint all Solidity code with Solhint.
- We document every contract, interface, library, function, and variable with comprehensive NatSpec comments.
- We annotate almost all lines in “src” with comments that explain what that bit of code does. Some annotations might come across as a little verbose, but we think that explicitness is important in financial software like smart contracts.
- We structure each unit test contract using a state tree, and use modifiers that follow the naming convention
when<Condition>
to replicate the tree in Solidity.