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 part of 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.vyormy_module_mock.vy), whilst the 🐍Vyper interface files.vyiuse 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,constantandimmutablevariable names use the screaming snake case notation (e.g.DEFAULT_CONSTANT) if no other notation is enforced via an EIP standard. internalconstant,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
internalfunctions where feasible to improve composability and modularity. - Unchecked arithmetic calculations should contain comments explaining why an overflow/underflow is guaranteed not to occur.
- Numeric literals should use underscores as thousand separators for readability (e.g.,
1_000_000instead of1000000). This applies to large constants, magic numbers, and any literal where readability would be improved. - Function decorators must follow this order:
- Visibility:
@external,@internal, or@deploy - Mutability:
@pure,@view, or@payable(the 🐍Vyper default mutability@nonpayableis always omitted if applicable) - Nonreentrancy locks:
@nonreentrant - Raw return:
@raw_return
- Visibility:
@external
@payable
@nonreentrant
@raw_return
def forward_call(target: address) -> Bytes[1_024]:
return raw_call(target, msg.data, max_outsize=1_024, value=msg.value)- All functions should be provided with full NatSpec comments containing the tags
@dev,@notice(if applicable),@paramfor each function parameter, and@returnif a return statement is present. - Please note the following order of layout:
- Pragma directives (one per line, in this order):
- 🐍Vyper version:
# pragma version ~=<vyper_version> - EVM version (if applicable):
# pragma evm-version <evm_version> - Optimisation mode (if applicable):
# pragma optimize <mode> - Nonreentrancy (if applicable):
# pragma nonreentrancy <flag> - Experimental code generation (=Venom) (if applicable):
# pragma experimental-codegen
- 🐍Vyper version:
- 🐍Vyper built-in interface
imports (one per line, withimplementson the next line if applicable) - Custom interface
imports (one per line, withimplementson the next line if applicable) - Module
imports (one per line, withinitializesoruseson the next line if applicable) - Module
exports publicconstantsinternalconstantspublicimmutablesinternalimmutablesflagdefinitionsstructdefinitionspublicstate variablesinternalstate variableseventdeclarations__init__function__default__functionexternalfunctionsinternalfunctions
- Pragma directives (one per line, in this order):
- There should be two line breaks between each top-level declaration, including
imports,constants,immutables,flags,structs, state variables,events, and functions. - 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 behaviour, please refer to 🐍Vyper's Official Style Guide and/or PEP 8 – Style Guide for Python Code.