- We write our contracts in Solidity.
- We build, test, format, and deploy the contracts with Foundry.
- We adhere to most of the 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 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 names are in either
PascalCase or camelCase
- Test file names end in
.t.sol, while scripts end in .s.sol
- We adhere to the Checks-Effects-Interactions and Function Requirements-Effects-Interactions + Protocol Invariants patterns (to the extent possible).
- We write concrete tests using Bulloak, adhering to the Branching Tree Technique.
- 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.