Skip to content

Latest commit

 

History

History
116 lines (90 loc) · 6.87 KB

File metadata and controls

116 lines (90 loc) · 6.87 KB

📑 Engineering Guidelines

✅ Testing

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.

🪅 Code Style

🐍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 Request (PR) Format

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.

🐍Vyper Conventions

  • The file names of module and mock contracts use the snake case notation (e.g. my_module.vy or my_module_mock.vy), whilst the 🐍Vyper interface files .vyi use the Pascal case notation prefixed with I (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 and immutable 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.
  • Numeric literals should use underscores as thousand separators for readability (e.g., 1_000_000 instead of 1000000). 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 @nonpayable is always omitted if applicable)
    • Nonreentrancy locks: @nonreentrant
    • Raw return: @raw_return
@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), @param for each function parameter, and @return if 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 built-in interface imports (one per line, with implements on the next line if applicable)
    • Custom interface imports (one per line, with implements on the next line if applicable)
    • Module imports (one per line, with initializes or uses on the next line if applicable)
    • Module exports
    • public constants
    • internal constants
    • public immutables
    • internal immutables
    • flag definitions
    • struct definitions
    • public state variables
    • internal state variables
    • event declarations
    • __init__ function
    • __default__ function
    • external functions
    • internal functions
  • 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.