Do not optimise for coverage, optimise for well-designed tests.
Write positive and negative unit tests.
- Write positive unit tests for things that the code should handle. Validate any states (including events) that change as a result of these tests.
- Write negative unit tests for things that the code should not handle. It is helpful to follow up on the positive test (as an adjacent test) and make the change needed to make it pass.
- Each code path should have its own unit test.
Any addition or change to the code must be accompanied by relevant and comprehensive tests. Refactors should avoid simultaneous changes to tests.
The test suite should run automatically for each change in the repository, and for pull requests, the tests must succeed before merging.
Please consider writing Foundry-based unit tests, property-based tests (i.e. stateless fuzzing), and invariant tests (i.e. stateful fuzzing) for all contracts, if applicable.
🐍Vyper code should be written in a consistent format that follows our 🐍Vyper Conventions.
Solidity test code should be written in a consistent format enforced by a prettier and linter that follows the official Solidity Style Guide. Also, we refer to Foundry's best practices.
The code should be simple and straightforward, with a focus on readability and comprehensibility. Consistency and predictability should be maintained throughout the code base. This is especially true for naming, which should be systematic, clear, and concise.
Wherever possible, modularity, composability, and gas efficiency should be pursued, but not at the expense of security compromises.
Pull requests are squash-merged in most cases to keep the main
branch history clean. The title of the PR becomes the commit message, so it should be written in a consistent format:
- Start with an emoji that well describes the PR. Please refer to the following emoji list as a basis:
- Bug Fix 🐛
- CI/CD 👷♂️
- Documentation 📖
- Events 🔊
- Gas Optimisation ⚡️
- New Feature 💥
- Nit 🥢
- Refactor/Cleanup ♻️
- Security Fix 🔒
- Use capitalisation for the PR title: "💥 Add Feature X" and not "💥 Add feature x".
- Do not end with a full stop (i.e. period).
- Write in the imperative: "💥 Add Feature X" and not "💥 Adds Feature X" or "💥 Added Feature X".
This repository does not follow conventional commits, so do not prefix the title with "fix:", "feat:", or similar. Also, pull requests in progress should be submitted as Drafts and should not be prefixed with "WIP:" or similar.
Branch names do not matter, and commit messages within a PR are mostly not important either, although they can support the review process.
- The file names of module and mock contracts use the snake case notation (e.g.
my_module.vy
ormy_module_mock.vy
), whilst the 🐍Vyper interface files.vyi
use the Pascal case notation prefixed withI
(e.g.IMyInterface.vyi
). - The names of
constant
,immutable
, and state variables, functions, and function parameters use the snake case notation (e.g.my_function
) if no other notation is enforced via an EIP standard. In particular,constant
andimmutable
variable names use the screaming snake case notation (e.g.DEFAULT_CONSTANT
) if no other notation is enforced via an EIP standard. internal
constant
,immutable
, state variables and functions must have an underscore prefix:
_SUPPORTED_INTERFACES: constant(bytes4[1]) = [0x01FFC9A7]
_CACHED_DOMAIN_SEPARATOR: immutable(bytes32)
_balances: HashMap[uint256, HashMap[address, uint256]]
@internal
@pure
def _as_singleton_array(element: uint256) -> DynArray[uint256, 1]:
return [element]
- Use
internal
functions where feasible to improve composability and modularity. - Unchecked arithmetic calculations should contain comments explaining why an overflow/underflow is guaranteed not to occur.
- All functions should be provided with full NatSpec comments containing the tags
@dev
,@notice
(if applicable),@param
for each function parameter, and@return
if a return statement is present. - Please note the following order of layout:
- Version pragma statement
- 🐍Vyper built-in interface imports
- Custom interface imports
- Module imports
- Module exports
public
constantsinternal
constantspublic
immutablesinternal
immutablespublic
state variablesinternal
state variablesevent
declarations__init__
function__default__
functionexternal
functionsinternal
functions
- There should be two line breaks between each variable or event declaration or function.
- Each line of code should be limited to a maximum of 120 characters, including spaces.
- Code comments should be confined to a maximum of 80 characters per line, including spaces, with an allowed exception for comments with long URL links.
- For any undocumented behavior, please refer to 🐍Vyper's Official Style Guide and/or PEP 8 – Style Guide for Python Code.